From 5cdfc227d2ee22e4c2bb93c960e89202efa4c87b Mon Sep 17 00:00:00 2001 From: albi3ro Date: Fri, 19 Apr 2024 14:41:57 -0400 Subject: [PATCH 01/10] experiment with performane improvements for dense matrix --- pennylane/pauli/pauli_arithmetic.py | 41 +++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/pennylane/pauli/pauli_arithmetic.py b/pennylane/pauli/pauli_arithmetic.py index 22e7616ab9f..74075185d40 100644 --- a/pennylane/pauli/pauli_arithmetic.py +++ b/pennylane/pauli/pauli_arithmetic.py @@ -904,6 +904,29 @@ def _to_sparse_mat(self, wire_order, buffer_size=None): matrix.eliminate_zeros() return matrix + def _to_dense_mat(self, wire_order): + """Compute the sparse matrix of the Pauli sentence by efficiently adding the Pauli words + that it is composed of. See pauli_sparse_matrices.md for the technical details.""" + pauli_words = list(self) # Ensure consistent ordering + + op_sparse_idx = _ps_to_sparse_index(pauli_words, wire_order) + _, unique_sparse_structures, unique_invs = np.unique( + op_sparse_idx, axis=0, return_index=True, return_inverse=True + ) + pw_sparse_structures = unique_sparse_structures[unique_invs] + + full_matrix = None + for sparse_structure in unique_sparse_structures: + indices, *_ = np.nonzero(pw_sparse_structures == sparse_structure) + mat = self._sum_same_structure_pws_dense([pauli_words[i] for i in indices], wire_order) + + if full_matrix is None: + full_matrix = mat + else: + full_matrix += mat + + return full_matrix + def dot(self, vector, wire_order=None): """Computes the matrix-vector product of the Pauli sentence with a state vector. See pauli_sparse_matrices.md for the technical details.""" @@ -946,6 +969,24 @@ def _get_same_structure_csr(self, pauli_words, wire_order): data = outer.T @ inner return indices, data.ravel() + def _sum_same_structure_pws_dense(self, pauli_words, wire_order): + matrix_size = 2 ** (len(wire_order)) + base_matrix = sparse.csr_matrix((matrix_size, matrix_size), dtype="complex128") + + data0 = pauli_words[0]._get_csr_data(wire_order, 1) + base_matrix.data = np.ones_like(data0) + base_matrix.indices = pauli_words[0]._get_csr_indices(wire_order) + base_matrix.indptr = _cached_arange( + matrix_size + 1 + ) # Non-zero entries by row (starting from 0) + base_matrix = base_matrix.toarray() + + data = self[pauli_words[0]] * data0 + for pw in pauli_words[1:]: + data += self[pw] * pw._get_csr_data(wire_order, 1) + + return data * base_matrix + def _sum_same_structure_pws(self, pauli_words, wire_order): """Sums Pauli words with the same sparse structure.""" mat = pauli_words[0].to_mat( From b4e1072059eee0dd4f2e32a635be066b673b637b Mon Sep 17 00:00:00 2001 From: albi3ro Date: Fri, 19 Apr 2024 14:43:08 -0400 Subject: [PATCH 02/10] merge master --- .github/stable/all_interfaces.txt | 59 +++++++++++----------- .github/stable/core.txt | 26 +++++----- .github/stable/doc.txt | 30 +++++------ .github/stable/external.txt | 84 +++++++++++++++---------------- .github/stable/jax.txt | 28 +++++------ .github/stable/tf.txt | 57 +++++++++++---------- .github/stable/torch.txt | 26 +++++----- 7 files changed, 153 insertions(+), 157 deletions(-) diff --git a/.github/stable/all_interfaces.txt b/.github/stable/all_interfaces.txt index 52e0088f742..6d533105ebf 100644 --- a/.github/stable/all_interfaces.txt +++ b/.github/stable/all_interfaces.txt @@ -3,70 +3,70 @@ appdirs==1.4.4 astunparse==1.6.3 autograd==1.6.2 autoray==0.6.9 -black==24.3.0 +black==24.4.0 cachetools==5.3.3 certifi==2024.2.2 cfgv==3.4.0 charset-normalizer==3.3.2 clarabel==0.7.1 click==8.1.7 -contourpy==1.2.0 +contourpy==1.2.1 coverage==7.4.4 cvxopt==1.3.2 -cvxpy==1.4.2 +cvxpy==1.4.3 cycler==0.12.1 distlib==0.3.8 ecos==2.0.13 exceptiongroup==1.2.0 -execnet==2.0.2 -filelock==3.13.3 +execnet==2.1.1 +filelock==3.13.4 flaky==3.8.1 -flatbuffers==24.3.7 -fonttools==4.50.0 +flatbuffers==24.3.25 +fonttools==4.51.0 fsspec==2024.3.1 future==1.0.0 gast==0.5.4 -google-auth==2.29.0 -google-auth-oauthlib==1.2.0 google-pasta==0.2.0 -grpcio==1.62.1 -h5py==3.10.0 +grpcio==1.62.2 +h5py==3.11.0 identify==2.5.35 -idna==3.6 +idna==3.7 importlib_metadata==7.1.0 importlib_resources==6.4.0 iniconfig==2.0.0 jax==0.4.23 jaxlib==0.4.23 Jinja2==3.1.3 -keras==2.15.0 +keras==3.2.1 kiwisolver==1.4.5 libclang==18.1.1 Markdown==3.6 +markdown-it-py==3.0.0 MarkupSafe==2.1.5 -matplotlib==3.8.3 +matplotlib==3.8.4 +mdurl==0.1.2 ml-dtypes==0.3.2 mpmath==1.3.0 mypy-extensions==1.0.0 +namex==0.0.8 networkx==3.2.1 nodeenv==1.8.0 numpy==1.26.4 -oauthlib==3.2.2 opt-einsum==3.3.0 +optree==0.11.0 osqp==0.6.5 packaging==24.0 pathspec==0.12.1 -PennyLane_Lightning==0.35.1 -pillow==10.2.0 +PennyLane_Lightning==0.36.0 +pillow==10.3.0 platformdirs==4.2.0 pluggy==1.4.0 pre-commit==3.7.0 protobuf==4.25.3 py==1.11.0 py-cpuinfo==9.0.0 -pyasn1==0.5.1 -pyasn1-modules==0.3.0 -pybind11==2.11.1 +pybind11==2.12.0 +Pygments==2.17.2 pyparsing==3.1.2 pytest==8.1.1 pytest-benchmark==4.0.0 @@ -77,28 +77,27 @@ pytest-xdist==3.5.0 python-dateutil==2.9.0.post0 pytorch-triton-rocm==2.2.0 PyYAML==6.0.1 -qdldl==0.1.7.post0 +qdldl==0.1.7.post2 requests==2.31.0 -requests-oauthlib==2.0.0 -rsa==4.9 +rich==13.7.1 rustworkx==0.14.2 scipy==1.11.4 scs==3.2.4.post1 semantic-version==2.10.0 six==1.16.0 sympy==1.12 -tensorboard==2.15.2 +tensorboard==2.16.2 tensorboard-data-server==0.7.2 -tensorflow==2.15.1 -tensorflow-estimator==2.15.0 +tensorflow==2.16.1 tensorflow-io-gcs-filesystem==0.36.0 termcolor==2.4.0 +tf_keras==2.16.0 toml==0.10.2 tomli==2.0.1 torch==2.2.0+rocm5.7 -typing_extensions==4.10.0 +typing_extensions==4.11.0 urllib3==2.2.1 -virtualenv==20.25.1 -Werkzeug==3.0.1 -wrapt==1.14.1 +virtualenv==20.25.3 +Werkzeug==3.0.2 +wrapt==1.16.0 zipp==3.18.1 diff --git a/.github/stable/core.txt b/.github/stable/core.txt index e3762014c32..dcb994f2262 100644 --- a/.github/stable/core.txt +++ b/.github/stable/core.txt @@ -1,32 +1,32 @@ appdirs==1.4.4 autograd==1.6.2 autoray==0.6.9 -black==24.3.0 +black==24.4.0 cachetools==5.3.3 certifi==2024.2.2 cfgv==3.4.0 charset-normalizer==3.3.2 clarabel==0.7.1 click==8.1.7 -contourpy==1.2.0 +contourpy==1.2.1 coverage==7.4.4 cvxopt==1.3.2 -cvxpy==1.4.2 +cvxpy==1.4.3 cycler==0.12.1 distlib==0.3.8 ecos==2.0.13 exceptiongroup==1.2.0 -execnet==2.0.2 -filelock==3.13.3 +execnet==2.1.1 +filelock==3.13.4 flaky==3.8.1 -fonttools==4.50.0 +fonttools==4.51.0 future==1.0.0 identify==2.5.35 -idna==3.6 +idna==3.7 importlib_resources==6.4.0 iniconfig==2.0.0 kiwisolver==1.4.5 -matplotlib==3.8.3 +matplotlib==3.8.4 mypy-extensions==1.0.0 networkx==3.2.1 nodeenv==1.8.0 @@ -35,13 +35,13 @@ osqp==0.6.5 packaging==24.0 pathspec==0.12.1 PennyLane_Lightning==0.36.0 -pillow==10.2.0 +pillow==10.3.0 platformdirs==4.2.0 pluggy==1.4.0 pre-commit==3.7.0 py==1.11.0 py-cpuinfo==9.0.0 -pybind11==2.11.1 +pybind11==2.12.0 pyparsing==3.1.2 pytest==8.1.1 pytest-benchmark==4.0.0 @@ -52,7 +52,7 @@ pytest-split==0.8.2 pytest-xdist==3.5.0 python-dateutil==2.9.0.post0 PyYAML==6.0.1 -qdldl==0.1.7.post0 +qdldl==0.1.7.post2 requests==2.31.0 rustworkx==0.14.2 scipy==1.11.4 @@ -61,7 +61,7 @@ semantic-version==2.10.0 six==1.16.0 toml==0.10.2 tomli==2.0.1 -typing_extensions==4.10.0 +typing_extensions==4.11.0 urllib3==2.2.1 -virtualenv==20.25.1 +virtualenv==20.25.3 zipp==3.18.1 diff --git a/.github/stable/doc.txt b/.github/stable/doc.txt index 778a88247df..a33e6e94e0c 100644 --- a/.github/stable/doc.txt +++ b/.github/stable/doc.txt @@ -1,5 +1,5 @@ absl-py==2.1.0 -aiohttp==3.9.3 +aiohttp==3.9.5 aiosignal==1.3.1 alabaster==0.7.16 appdirs==1.4.4 @@ -13,14 +13,14 @@ cachetools==5.3.3 certifi==2024.2.2 charset-normalizer==3.3.2 cirq-core==1.3.0 -contourpy==1.2.0 +contourpy==1.2.1 cycler==0.12.1 deprecation==2.1.0 docutils==0.16 duet==0.2.9 -exceptiongroup==1.2.0 +exceptiongroup==1.2.1 flatbuffers==24.3.25 -fonttools==4.50.0 +fonttools==4.51.0 frozenlist==1.4.1 fsspec==2024.3.1 future==1.0.0 @@ -29,9 +29,9 @@ google-auth==2.29.0 google-auth-oauthlib==0.4.6 google-pasta==0.2.0 graphviz==0.20.3 -grpcio==1.62.1 -h5py==3.10.0 -idna==3.6 +grpcio==1.62.2 +h5py==3.11.0 +idna==3.7 imagesize==1.4.1 importlib_metadata==7.1.0 importlib_resources==6.4.0 @@ -48,7 +48,7 @@ Markdown==3.6 MarkupSafe==2.1.5 matplotlib==3.8.0 mistune==0.8.4 -ml-dtypes==0.3.2 +ml-dtypes==0.4.0 mpmath==1.3.0 multidict==6.0.5 networkx==2.6 @@ -58,15 +58,15 @@ openfermion==1.6.1 openfermionpyscf==0.5 opt-einsum==3.3.0 packaging==24.0 -pandas==2.2.1 +pandas==2.2.2 pennylane-sphinx-theme==0.5.5 PennyLane_Lightning==0.35.1 -pillow==10.2.0 +pillow==10.3.0 pluggy==1.4.0 protobuf==3.19.6 PubChemPy==1.0.4 -pyasn1==0.5.1 -pyasn1-modules==0.3.0 +pyasn1==0.6.0 +pyasn1_modules==0.4.0 pybtex==0.24.0 pybtex-docutils==1.0.3 Pygments==2.17.2 @@ -81,7 +81,7 @@ requests==2.28.2 requests-oauthlib==2.0.0 rsa==4.9 rustworkx==0.12.1 -scipy==1.12.0 +scipy==1.13.0 semantic-version==2.10.0 six==1.16.0 snowballstemmer==2.2.0 @@ -111,10 +111,10 @@ toml==0.10.2 tomli==2.0.1 torch==1.9.0+cpu tqdm==4.66.2 -typing_extensions==4.10.0 +typing_extensions==4.11.0 tzdata==2024.1 urllib3==1.26.18 -Werkzeug==3.0.1 +Werkzeug==3.0.2 wrapt==1.16.0 xanadu-sphinx-theme==0.5.0 yarl==1.9.4 diff --git a/.github/stable/external.txt b/.github/stable/external.txt index 65b3050b51d..e4e2acf314c 100644 --- a/.github/stable/external.txt +++ b/.github/stable/external.txt @@ -12,7 +12,7 @@ autograd==1.6.2 autoray==0.6.9 Babel==2.14.0 beautifulsoup4==4.12.3 -black==24.3.0 +black==24.4.0 bleach==6.1.0 cachetools==5.3.3 certifi==2024.2.2 @@ -22,10 +22,10 @@ charset-normalizer==3.3.2 clarabel==0.7.1 click==8.1.7 comm==0.2.2 -contourpy==1.2.0 +contourpy==1.2.1 coverage==7.4.4 cvxopt==1.3.2 -cvxpy==1.4.2 +cvxpy==1.4.3 cycler==0.12.1 debugpy==1.8.1 decorator==5.1.1 @@ -34,30 +34,28 @@ diastatic-malt==2.15.1 distlib==0.3.8 ecos==2.0.13 exceptiongroup==1.2.0 -execnet==2.0.2 +execnet==2.1.1 executing==2.0.1 fastjsonschema==2.19.1 -filelock==3.13.3 +filelock==3.13.4 flaky==3.8.1 -flatbuffers==24.3.7 -fonttools==4.50.0 +flatbuffers==24.3.25 +fonttools==4.51.0 fqdn==1.5.1 future==1.0.0 gast==0.5.4 -google-auth==2.29.0 -google-auth-oauthlib==1.2.0 google-pasta==0.2.0 -grpcio==1.62.1 +grpcio==1.62.2 h11==0.14.0 -h5py==3.10.0 -httpcore==1.0.4 +h5py==3.11.0 +httpcore==1.0.5 httpx==0.27.0 identify==2.5.35 -idna==3.6 +idna==3.7 importlib_metadata==7.1.0 importlib_resources==6.4.0 iniconfig==2.0.0 -ipykernel==6.29.3 +ipykernel==6.29.4 ipython==8.18.1 ipython-genutils==0.2.0 ipywidgets==7.8.1 @@ -66,52 +64,55 @@ jax==0.4.23 jaxlib==0.4.23 jedi==0.19.1 Jinja2==3.1.3 -json5==0.9.24 +json5==0.9.25 jsonpointer==2.4 jsonschema==4.21.1 jsonschema-specifications==2023.12.1 jupyter-events==0.10.0 -jupyter-lsp==2.2.4 +jupyter-lsp==2.2.5 jupyter_client==8.6.1 jupyter_core==5.7.2 -jupyter_server==2.13.0 +jupyter_server==2.14.0 jupyter_server_terminals==0.5.3 -jupyterlab==4.1.5 +jupyterlab==4.1.6 jupyterlab-widgets==1.1.7 jupyterlab_pygments==0.3.0 -jupyterlab_server==2.25.4 -keras==2.15.0 +jupyterlab_server==2.26.0 +keras==3.2.1 kiwisolver==1.4.5 lark==1.1.9 libclang==18.1.1 Markdown==3.6 +markdown-it-py==3.0.0 MarkupSafe==2.1.5 -matplotlib==3.8.3 -matplotlib-inline==0.1.6 +matplotlib==3.8.4 +matplotlib-inline==0.1.7 +mdurl==0.1.2 mistune==3.0.2 ml-dtypes==0.3.2 mypy-extensions==1.0.0 +namex==0.0.8 nbclient==0.10.0 nbconvert==7.16.3 -nbformat==5.10.3 +nbformat==5.10.4 nest-asyncio==1.6.0 networkx==3.2.1 nodeenv==1.8.0 -notebook==7.1.2 +notebook==7.1.3 notebook_shim==0.2.4 numpy==1.26.4 -oauthlib==3.2.2 opt-einsum==3.3.0 +optree==0.11.0 osqp==0.6.5 overrides==7.7.0 packaging==24.0 pandocfilters==1.5.1 -parso==0.8.3 +parso==0.8.4 pathspec==0.12.1 PennyLane-Catalyst==0.5.0 PennyLane_Lightning==0.35.1 pexpect==4.9.0 -pillow==10.2.0 +pillow==10.3.0 platformdirs==4.2.0 pluggy==1.4.0 pre-commit==3.7.0 @@ -123,10 +124,8 @@ ptyprocess==0.7.0 pure-eval==0.2.2 py==1.11.0 py-cpuinfo==9.0.0 -pyasn1==0.5.1 -pyasn1-modules==0.3.0 -pybind11==2.11.1 -pycparser==2.21 +pybind11==2.12.0 +pycparser==2.22 Pygments==2.17.2 pyparsing==3.1.2 pyperclip==1.8.2 @@ -139,33 +138,32 @@ pytest-xdist==3.5.0 python-dateutil==2.9.0.post0 python-json-logger==2.0.7 PyYAML==6.0.1 -pyzmq==25.1.2 +pyzmq==26.0.0 pyzx==0.8.0 -qdldl==0.1.7.post0 +qdldl==0.1.7.post2 referencing==0.34.0 requests==2.31.0 -requests-oauthlib==2.0.0 rfc3339-validator==0.1.4 rfc3986-validator==0.1.1 +rich==13.7.1 rpds-py==0.18.0 -rsa==4.9 rustworkx==0.14.2 scipy==1.11.4 scs==3.2.4.post1 semantic-version==2.10.0 -Send2Trash==1.8.2 +Send2Trash==1.8.3 six==1.16.0 sniffio==1.3.1 soupsieve==2.5 stack-data==0.6.3 stim==1.13.0 -tensorboard==2.15.2 +tensorboard==2.16.2 tensorboard-data-server==0.7.2 -tensorflow==2.15.1 -tensorflow-estimator==2.15.0 +tensorflow==2.16.1 tensorflow-io-gcs-filesystem==0.36.0 termcolor==2.4.0 terminado==0.18.1 +tf_keras==2.16.0 tinycss2==1.2.1 toml==0.10.2 tomli==2.0.1 @@ -174,15 +172,15 @@ tornado==6.4 tqdm==4.66.2 traitlets==5.14.2 types-python-dateutil==2.9.0.20240316 -typing_extensions==4.10.0 +typing_extensions==4.11.0 uri-template==1.3.0 urllib3==2.2.1 -virtualenv==20.25.1 +virtualenv==20.25.3 wcwidth==0.2.13 webcolors==1.13 webencodings==0.5.1 websocket-client==1.7.0 -Werkzeug==3.0.1 +Werkzeug==3.0.2 widgetsnbextension==3.6.6 -wrapt==1.14.1 +wrapt==1.16.0 zipp==3.18.1 diff --git a/.github/stable/jax.txt b/.github/stable/jax.txt index a5f8e731ef2..b829735c4d4 100644 --- a/.github/stable/jax.txt +++ b/.github/stable/jax.txt @@ -1,36 +1,36 @@ appdirs==1.4.4 autograd==1.6.2 autoray==0.6.9 -black==24.3.0 +black==24.4.0 cachetools==5.3.3 certifi==2024.2.2 cfgv==3.4.0 charset-normalizer==3.3.2 clarabel==0.7.1 click==8.1.7 -contourpy==1.2.0 +contourpy==1.2.1 coverage==7.4.4 cvxopt==1.3.2 -cvxpy==1.4.2 +cvxpy==1.4.3 cycler==0.12.1 distlib==0.3.8 ecos==2.0.13 exceptiongroup==1.2.0 -execnet==2.0.2 -filelock==3.13.3 +execnet==2.1.1 +filelock==3.13.4 flaky==3.8.1 -fonttools==4.50.0 +fonttools==4.51.0 future==1.0.0 identify==2.5.35 -idna==3.6 +idna==3.7 importlib_metadata==7.1.0 importlib_resources==6.4.0 iniconfig==2.0.0 jax==0.4.23 jaxlib==0.4.23 kiwisolver==1.4.5 -matplotlib==3.8.3 -ml-dtypes==0.3.2 +matplotlib==3.8.4 +ml-dtypes==0.4.0 mypy-extensions==1.0.0 networkx==3.2.1 nodeenv==1.8.0 @@ -40,13 +40,13 @@ osqp==0.6.5 packaging==24.0 pathspec==0.12.1 PennyLane_Lightning==0.36.0 -pillow==10.2.0 +pillow==10.3.0 platformdirs==4.2.0 pluggy==1.4.0 pre-commit==3.7.0 py==1.11.0 py-cpuinfo==9.0.0 -pybind11==2.11.1 +pybind11==2.12.0 pyparsing==3.1.2 pytest==8.1.1 pytest-benchmark==4.0.0 @@ -57,7 +57,7 @@ pytest-split==0.8.2 pytest-xdist==3.5.0 python-dateutil==2.9.0.post0 PyYAML==6.0.1 -qdldl==0.1.7.post0 +qdldl==0.1.7.post2 requests==2.31.0 rustworkx==0.14.2 scipy==1.11.4 @@ -66,7 +66,7 @@ semantic-version==2.10.0 six==1.16.0 toml==0.10.2 tomli==2.0.1 -typing_extensions==4.10.0 +typing_extensions==4.11.0 urllib3==2.2.1 -virtualenv==20.25.1 +virtualenv==20.25.3 zipp==3.18.1 diff --git a/.github/stable/tf.txt b/.github/stable/tf.txt index c03ef4274ff..af60383c673 100644 --- a/.github/stable/tf.txt +++ b/.github/stable/tf.txt @@ -3,65 +3,65 @@ appdirs==1.4.4 astunparse==1.6.3 autograd==1.6.2 autoray==0.6.9 -black==24.3.0 +black==24.4.0 cachetools==5.3.3 certifi==2024.2.2 cfgv==3.4.0 charset-normalizer==3.3.2 clarabel==0.7.1 click==8.1.7 -contourpy==1.2.0 +contourpy==1.2.1 coverage==7.4.4 cvxopt==1.3.2 -cvxpy==1.4.2 +cvxpy==1.4.3 cycler==0.12.1 distlib==0.3.8 ecos==2.0.13 exceptiongroup==1.2.0 -execnet==2.0.2 -filelock==3.13.3 +execnet==2.1.1 +filelock==3.13.4 flaky==3.8.1 -flatbuffers==24.3.7 -fonttools==4.50.0 +flatbuffers==24.3.25 +fonttools==4.51.0 future==1.0.0 gast==0.5.4 -google-auth==2.29.0 -google-auth-oauthlib==1.2.0 google-pasta==0.2.0 -grpcio==1.62.1 -h5py==3.10.0 +grpcio==1.62.2 +h5py==3.11.0 identify==2.5.35 -idna==3.6 +idna==3.7 importlib_metadata==7.1.0 importlib_resources==6.4.0 iniconfig==2.0.0 -keras==2.15.0 +keras==3.2.1 kiwisolver==1.4.5 libclang==18.1.1 Markdown==3.6 +markdown-it-py==3.0.0 MarkupSafe==2.1.5 -matplotlib==3.8.3 +matplotlib==3.8.4 +mdurl==0.1.2 ml-dtypes==0.3.2 mypy-extensions==1.0.0 +namex==0.0.8 networkx==3.2.1 nodeenv==1.8.0 numpy==1.26.4 -oauthlib==3.2.2 opt-einsum==3.3.0 +optree==0.11.0 osqp==0.6.5 packaging==24.0 pathspec==0.12.1 PennyLane_Lightning==0.36.0 -pillow==10.2.0 +pillow==10.3.0 platformdirs==4.2.0 pluggy==1.4.0 pre-commit==3.7.0 protobuf==4.25.3 py==1.11.0 py-cpuinfo==9.0.0 -pyasn1==0.5.1 -pyasn1-modules==0.3.0 -pybind11==2.11.1 +pybind11==2.12.0 +Pygments==2.17.2 pyparsing==3.1.2 pytest==8.1.1 pytest-benchmark==4.0.0 @@ -72,26 +72,25 @@ pytest-split==0.8.2 pytest-xdist==3.5.0 python-dateutil==2.9.0.post0 PyYAML==6.0.1 -qdldl==0.1.7.post0 +qdldl==0.1.7.post2 requests==2.31.0 -requests-oauthlib==2.0.0 -rsa==4.9 +rich==13.7.1 rustworkx==0.14.2 scipy==1.11.4 scs==3.2.4.post1 semantic-version==2.10.0 six==1.16.0 -tensorboard==2.15.2 +tensorboard==2.16.2 tensorboard-data-server==0.7.2 -tensorflow==2.15.1 -tensorflow-estimator==2.15.0 +tensorflow==2.16.1 tensorflow-io-gcs-filesystem==0.36.0 termcolor==2.4.0 +tf_keras==2.16.0 toml==0.10.2 tomli==2.0.1 -typing_extensions==4.10.0 +typing_extensions==4.11.0 urllib3==2.2.1 -virtualenv==20.25.1 -Werkzeug==3.0.1 -wrapt==1.14.1 +virtualenv==20.25.3 +Werkzeug==3.0.2 +wrapt==1.16.0 zipp==3.18.1 diff --git a/.github/stable/torch.txt b/.github/stable/torch.txt index d35f83648cc..8cc344df11f 100644 --- a/.github/stable/torch.txt +++ b/.github/stable/torch.txt @@ -1,35 +1,35 @@ appdirs==1.4.4 autograd==1.6.2 autoray==0.6.9 -black==24.3.0 +black==24.4.0 cachetools==5.3.3 certifi==2024.2.2 cfgv==3.4.0 charset-normalizer==3.3.2 clarabel==0.7.1 click==8.1.7 -contourpy==1.2.0 +contourpy==1.2.1 coverage==7.4.4 cvxopt==1.3.2 -cvxpy==1.4.2 +cvxpy==1.4.3 cycler==0.12.1 distlib==0.3.8 ecos==2.0.13 exceptiongroup==1.2.0 -execnet==2.0.2 -filelock==3.13.3 +execnet==2.1.1 +filelock==3.13.4 flaky==3.8.1 -fonttools==4.50.0 +fonttools==4.51.0 fsspec==2024.3.1 future==1.0.0 identify==2.5.35 -idna==3.6 +idna==3.7 importlib_resources==6.4.0 iniconfig==2.0.0 Jinja2==3.1.3 kiwisolver==1.4.5 MarkupSafe==2.1.5 -matplotlib==3.8.3 +matplotlib==3.8.4 mpmath==1.3.0 mypy-extensions==1.0.0 networkx==3.2.1 @@ -39,13 +39,13 @@ osqp==0.6.5 packaging==24.0 pathspec==0.12.1 PennyLane_Lightning==0.36.0 -pillow==10.2.0 +pillow==10.3.0 platformdirs==4.2.0 pluggy==1.4.0 pre-commit==3.7.0 py==1.11.0 py-cpuinfo==9.0.0 -pybind11==2.11.1 +pybind11==2.12.0 pyparsing==3.1.2 pytest==8.1.1 pytest-benchmark==4.0.0 @@ -56,7 +56,7 @@ pytest-xdist==3.5.0 python-dateutil==2.9.0.post0 pytorch-triton-rocm==2.2.0 PyYAML==6.0.1 -qdldl==0.1.7.post0 +qdldl==0.1.7.post2 requests==2.31.0 rustworkx==0.14.2 scipy==1.11.4 @@ -67,7 +67,7 @@ sympy==1.12 toml==0.10.2 tomli==2.0.1 torch==2.2.0+rocm5.7 -typing_extensions==4.10.0 +typing_extensions==4.11.0 urllib3==2.2.1 -virtualenv==20.25.1 +virtualenv==20.25.3 zipp==3.18.1 From 7906d871215be41e69feac02ee958966e1805752 Mon Sep 17 00:00:00 2001 From: albi3ro Date: Thu, 25 Apr 2024 09:35:33 -0400 Subject: [PATCH 03/10] improve the performance of pauli sentence dense matrices --- pennylane/ops/op_math/prod.py | 2 ++ pennylane/ops/op_math/sum.py | 2 ++ pennylane/pauli/pauli_arithmetic.py | 55 ++++------------------------- 3 files changed, 11 insertions(+), 48 deletions(-) diff --git a/pennylane/ops/op_math/prod.py b/pennylane/ops/op_math/prod.py index 94f1df746ec..3d70944bae5 100644 --- a/pennylane/ops/op_math/prod.py +++ b/pennylane/ops/op_math/prod.py @@ -294,6 +294,8 @@ def decomposition(self): def matrix(self, wire_order=None): """Representation of the operator as a matrix in the computational basis.""" + if self.pauli_rep: + return self.pauli_rep.to_mat(wire_order=wire_order or self.wires) mats: List[TensorLike] = [] batched: List[bool] = [] # batched[i] tells if mats[i] is batched or not diff --git a/pennylane/ops/op_math/sum.py b/pennylane/ops/op_math/sum.py index cb9a580f935..66ba5451c02 100644 --- a/pennylane/ops/op_math/sum.py +++ b/pennylane/ops/op_math/sum.py @@ -318,6 +318,8 @@ def matrix(self, wire_order=None): Returns: tensor_like: matrix representation """ + if self.pauli_rep: + return self.pauli_rep.to_mat(wire_order=wire_order or self.wires) gen = ( (qml.matrix(op) if isinstance(op, qml.ops.Hamiltonian) else op.matrix(), op.wires) for op in self diff --git a/pennylane/pauli/pauli_arithmetic.py b/pennylane/pauli/pauli_arithmetic.py index 74075185d40..4f5e5a2baed 100644 --- a/pennylane/pauli/pauli_arithmetic.py +++ b/pennylane/pauli/pauli_arithmetic.py @@ -15,7 +15,6 @@ # pylint:disable=protected-access from copy import copy from functools import reduce, lru_cache -from typing import Iterable import numpy as np from scipy import sparse @@ -820,51 +819,15 @@ def to_mat(self, wire_order=None, format="dense", buffer_size=None): ValueError: Can't get the matrix of an empty PauliSentence. """ wire_order = self.wires if wire_order is None else Wires(wire_order) - if not wire_order.contains_wires(self.wires): - raise ValueError( - "Can't get the matrix for the specified wire order because it " - f"does not contain all the Pauli sentence's wires {self.wires}" - ) - - def _pw_wires(w: Iterable) -> Wires: - """Return the native Wires instance for a list of wire labels. - w represents the wires of the PauliWord being processed. In case - the PauliWord is empty ({}), choose any arbitrary wire from the - PauliSentence it is composed in. - """ - if w: - # PauliWord is not empty, so we can use its wires - return Wires(w) - - if wire_order: - # PauliWord is empty, treat it as Identity operator on any wire - # Pick any arbitrary wire from wire_order - return Wires(wire_order[0]) - - return wire_order - if len(self) == 0: n = len(wire_order) if wire_order is not None else 0 if format == "dense": return np.zeros((2**n, 2**n)) return sparse.csr_matrix((2**n, 2**n), dtype="complex128") - if format != "dense": - return self._to_sparse_mat(wire_order, buffer_size=buffer_size) - - mats_and_wires_gen = ( - ( - coeff * pw.to_mat(wire_order=_pw_wires(pw.wires), format=format), - _pw_wires(pw.wires), - ) - for pw, coeff in self.items() - ) - - reduced_mat, result_wire_order = math.reduce_matrices( - mats_and_wires_gen=mats_and_wires_gen, reduce_func=math.add - ) - - return math.expand_matrix(reduced_mat, result_wire_order, wire_order=wire_order) + if format == "dense": + return self._to_dense_mat(wire_order) + return self._to_sparse_mat(wire_order, buffer_size=buffer_size) def _to_sparse_mat(self, wire_order, buffer_size=None): """Compute the sparse matrix of the Pauli sentence by efficiently adding the Pauli words @@ -920,11 +883,7 @@ def _to_dense_mat(self, wire_order): indices, *_ = np.nonzero(pw_sparse_structures == sparse_structure) mat = self._sum_same_structure_pws_dense([pauli_words[i] for i in indices], wire_order) - if full_matrix is None: - full_matrix = mat - else: - full_matrix += mat - + full_matrix = mat if full_matrix is None else qml.math.add(full_matrix, mat) return full_matrix def dot(self, vector, wire_order=None): @@ -980,12 +939,12 @@ def _sum_same_structure_pws_dense(self, pauli_words, wire_order): matrix_size + 1 ) # Non-zero entries by row (starting from 0) base_matrix = base_matrix.toarray() - - data = self[pauli_words[0]] * data0 + coeff = self[pauli_words[0]] + data = coeff * qml.math.convert_like(data0, coeff) for pw in pauli_words[1:]: data += self[pw] * pw._get_csr_data(wire_order, 1) - return data * base_matrix + return qml.math.einsum("ij,i->ij", base_matrix, data) def _sum_same_structure_pws(self, pauli_words, wire_order): """Sums Pauli words with the same sparse structure.""" From 2c85197e825e991a120d469d247541c6ccf72ad8 Mon Sep 17 00:00:00 2001 From: albi3ro Date: Thu, 25 Apr 2024 13:50:48 -0400 Subject: [PATCH 04/10] fixing up various tests --- pennylane/pauli/pauli_arithmetic.py | 28 ++++++++++++++++++--- tests/devices/qubit/test_apply_operation.py | 12 ++++++--- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/pennylane/pauli/pauli_arithmetic.py b/pennylane/pauli/pauli_arithmetic.py index 99a38e74fc4..490c441b68d 100644 --- a/pennylane/pauli/pauli_arithmetic.py +++ b/pennylane/pauli/pauli_arithmetic.py @@ -454,6 +454,8 @@ def _get_csr_data(self, wire_order, coeff): full_word = [self[wire] for wire in wire_order] matrix_size = 2 ** len(wire_order) + if len(self) == 0: + return np.full(matrix_size, coeff, dtype=np.complex128) data = np.empty(matrix_size, dtype=np.complex128) # Non-zero values current_size = 2 data[:current_size], _ = _cached_sparse_data(full_word[-1]) @@ -485,6 +487,8 @@ def _get_csr_indices(self, wire_order): """Computes the sparse matrix indices of the Pauli word times a coefficient, given a wire order.""" full_word = [self[wire] for wire in wire_order] matrix_size = 2 ** len(wire_order) + if len(self) == 0: + return _cached_arange(matrix_size + 1) indices = np.empty(matrix_size, dtype=np.int64) # Column index of non-zero values current_size = 2 _, indices[:current_size] = _cached_sparse_data(full_word[-1]) @@ -890,7 +894,13 @@ def _to_dense_mat(self, wire_order): that it is composed of. See pauli_sparse_matrices.md for the technical details.""" pauli_words = list(self) # Ensure consistent ordering - op_sparse_idx = _ps_to_sparse_index(pauli_words, wire_order) + try: + op_sparse_idx = _ps_to_sparse_index(pauli_words, wire_order) + except qml.wires.WireError as e: + raise ValueError( + "Can't get the matrix for the specified wire order because it " + f"does not contain all the Pauli sentence's wires {self.wires}" + ) from e _, unique_sparse_structures, unique_invs = np.unique( op_sparse_idx, axis=0, return_index=True, return_inverse=True ) @@ -958,9 +968,21 @@ def _sum_same_structure_pws_dense(self, pauli_words, wire_order): ) # Non-zero entries by row (starting from 0) base_matrix = base_matrix.toarray() coeff = self[pauli_words[0]] - data = coeff * qml.math.convert_like(data0, coeff) + ml_interface = qml.math.get_interface(coeff) + if ml_interface == "torch": + data0 = qml.math.convert_like(data0, coeff) + elif ml_interface == "tf": + data0 = qml.math.cast_like(data0, coeff) + data = coeff * data0 for pw in pauli_words[1:]: - data += self[pw] * pw._get_csr_data(wire_order, 1) + coeff = self[pw] + csr_data = pw._get_csr_data(wire_order, 1) + ml_interface = qml.math.get_interface(coeff) + if ml_interface == "torch": + csr_data = qml.math.convert_like(csr_data, coeff) + elif ml_interface == "tf": + csr_data = qml.math.cast_like(csr_data, coeff) + data += self[pw] * csr_data return qml.math.einsum("ij,i->ij", base_matrix, data) diff --git a/tests/devices/qubit/test_apply_operation.py b/tests/devices/qubit/test_apply_operation.py index c411c03ba7a..2f73e465e6e 100644 --- a/tests/devices/qubit/test_apply_operation.py +++ b/tests/devices/qubit/test_apply_operation.py @@ -389,7 +389,8 @@ def test_large_state_small_matrix_evolves_matrix(self, mocker): # seems like _evolve_state_vector_under_parametrized_evolution calls # einsum twice, and the default apply_operation only once - assert spy.call_count == 1 + # and it seems that getting the matrix from the hamiltonian calls einsum a few times. + assert spy.call_count == 6 def test_small_evolves_state(self, mocker): """Test that applying a ParametrizedEvolution operating on less @@ -465,7 +466,8 @@ def test_small_evolves_state(self, mocker): # seems like _evolve_state_vector_under_parametrized_evolution calls # einsum twice, and the default apply_operation only once - assert spy.call_count == 2 + # and it seems that getting the matrix from the hamiltonian calls einsum a few times. + assert spy.call_count == 7 def test_parametrized_evolution_raises_error(self): """Test applying a ParametrizedEvolution without params or t specified raises an error.""" @@ -530,9 +532,11 @@ def test_with_batched_state(self, num_state_wires, mocker): assert np.allclose(new_state, new_state_expected, atol=0.002) if num_state_wires == 4: - assert spy_einsum.call_count == 2 + # and it seems that getting the matrix from the hamiltonian calls einsum a few times. + assert spy_einsum.call_count == 7 else: - assert spy_einsum.call_count == 1 + # and it seems that getting the matrix from the hamiltonian calls einsum a few times. + assert spy_einsum.call_count == 6 @pytest.mark.parametrize("ml_framework", ml_frameworks_list) From 230e5e830378e7bdd200eab0d29a8d6358d06bef Mon Sep 17 00:00:00 2001 From: Christina Lee Date: Thu, 25 Apr 2024 13:51:17 -0400 Subject: [PATCH 05/10] Update pennylane/pauli/pauli_arithmetic.py Co-authored-by: Mudit Pandey --- pennylane/pauli/pauli_arithmetic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/pauli/pauli_arithmetic.py b/pennylane/pauli/pauli_arithmetic.py index 490c441b68d..e6bf7d3eb30 100644 --- a/pennylane/pauli/pauli_arithmetic.py +++ b/pennylane/pauli/pauli_arithmetic.py @@ -890,7 +890,7 @@ def _to_sparse_mat(self, wire_order, buffer_size=None): return matrix def _to_dense_mat(self, wire_order): - """Compute the sparse matrix of the Pauli sentence by efficiently adding the Pauli words + """Compute the dense matrix of the Pauli sentence by efficiently adding the Pauli words that it is composed of. See pauli_sparse_matrices.md for the technical details.""" pauli_words = list(self) # Ensure consistent ordering From 23ee7104d72761cbc64d0626fdbefdeddbf9789f Mon Sep 17 00:00:00 2001 From: albi3ro Date: Thu, 25 Apr 2024 14:25:47 -0400 Subject: [PATCH 06/10] fix error --- pennylane/pauli/pauli_arithmetic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/pauli/pauli_arithmetic.py b/pennylane/pauli/pauli_arithmetic.py index 490c441b68d..ff3cc8fe0e0 100644 --- a/pennylane/pauli/pauli_arithmetic.py +++ b/pennylane/pauli/pauli_arithmetic.py @@ -488,7 +488,7 @@ def _get_csr_indices(self, wire_order): full_word = [self[wire] for wire in wire_order] matrix_size = 2 ** len(wire_order) if len(self) == 0: - return _cached_arange(matrix_size + 1) + return _cached_arange(matrix_size) indices = np.empty(matrix_size, dtype=np.int64) # Column index of non-zero values current_size = 2 _, indices[:current_size] = _cached_sparse_data(full_word[-1]) From b1b58f23a283eac62e432468d1c164111f7300b7 Mon Sep 17 00:00:00 2001 From: albi3ro Date: Fri, 26 Apr 2024 14:48:01 -0400 Subject: [PATCH 07/10] more tests --- tests/pauli/test_pauli_arithmetic.py | 252 ++++++++++++++++++--------- 1 file changed, 165 insertions(+), 87 deletions(-) diff --git a/tests/pauli/test_pauli_arithmetic.py b/tests/pauli/test_pauli_arithmetic.py index 67d0c40db98..62229838b0c 100644 --- a/tests/pauli/test_pauli_arithmetic.py +++ b/tests/pauli/test_pauli_arithmetic.py @@ -901,93 +901,6 @@ def test_iadd_ps_pw(self, ps, pw, res): assert copy_ps1 == res # Check if the modified object matches the expected result assert copy_ps2 == ps # Ensure the original object is not modified - PS_EMPTY_CASES = ( - (PauliSentence({}), np.zeros((1, 1))), - (PauliSentence({PauliWord({}): 1.0}), np.ones((1, 1))), - (PauliSentence({PauliWord({}): 2.5}), 2.5 * np.ones((1, 1))), - ( - PauliSentence({PauliWord({}): 2.5, PauliWord({0: "X"}): 1.0}), - 2.5 * np.eye(2) + qml.matrix(qml.PauliX(0)), - ), - ) - - @pytest.mark.parametrize("ps, true_res", PS_EMPTY_CASES) - def test_to_mat_empty(self, ps, true_res): - """Test that empty PauliSentences and PauliSentences with empty PauliWords are handled correctly""" - - res_dense = ps.to_mat() - assert np.allclose(res_dense, true_res) - res_sparse = ps.to_mat(format="csr") - assert sparse.issparse(res_sparse) - assert qml.math.allclose(res_sparse.todense(), true_res) - - def test_empty_pauli_to_mat_with_wire_order(self): - """Test the to_mat method with an empty PauliSentence and PauliWord and an external wire order.""" - actual = PauliSentence({PauliWord({}): 1.5}).to_mat([0, 1]) - assert np.allclose(actual, 1.5 * np.eye(4)) - - ps_wire_order = ((ps1, []), (ps1, [0, 1, 2, "a", "b"]), (ps3, [0, 1, "c"])) - - @pytest.mark.parametrize("ps, wire_order", ps_wire_order) - def test_to_mat_error_incomplete(self, ps, wire_order): - """Test that an appropriate error is raised when the wire order does - not contain all the PauliSentence's wires.""" - match = "Can't get the matrix for the specified wire order" - with pytest.raises(ValueError, match=match): - ps.to_mat(wire_order=wire_order) - - def test_to_mat_empty_sentence_with_wires(self): - """Test that a zero matrix is returned if wire_order is provided on an empty PauliSentence.""" - true_res = np.zeros((4, 4)) - res_dense = ps5.to_mat(wire_order=[0, 1]) - assert np.allclose(res_dense, true_res) - res_sparse = ps5.to_mat(wire_order=[0, 1], format="csr") - assert sparse.issparse(res_sparse) - assert qml.math.allclose(res_sparse.todense(), true_res) - - tup_ps_mat = ( - ( - ps1, - [0, 1, 2, "a", "b", "c"], - 1.23 * np.kron(np.kron(matI, np.kron(matX, matY)), np.eye(8)) - + 4j * np.kron(np.eye(8), np.kron(matX, np.kron(matX, matZ))) - - 0.5 * np.kron(matZ, np.kron(np.eye(8), np.kron(matZ, matZ))), - ), - ( - ps2, - ["a", "b", "c", 0, 1, 2], - -1.23 * np.kron(np.eye(8), np.kron(matI, np.kron(matX, matY))) - - 4j * np.kron(np.kron(matX, np.kron(matX, matZ)), np.eye(8)) - + 0.5 * np.kron(np.kron(matI, np.kron(matZ, np.kron(matZ, matZ))), np.eye(4)), - ), - ( - ps3, - [0, "b", "c"], - -0.5 * np.kron(matZ, np.kron(matZ, matZ)) + 1 * np.eye(8), - ), - ) - - @pytest.mark.parametrize("ps, wire_order, true_matrix", tup_ps_mat) - def test_to_mat_wire_order(self, ps, wire_order, true_matrix): - """Test that the wire_order is correctly incorporated in computing the - matrix representation.""" - assert np.allclose(ps.to_mat(wire_order), true_matrix) - - @pytest.mark.parametrize("ps, wire_order, true_matrix", tup_ps_mat) - def test_to_mat_format(self, ps, wire_order, true_matrix): - """Test that the correct type of matrix is returned given the format kwarg.""" - sparse_mat = ps.to_mat(wire_order, format="csr") - assert sparse.issparse(sparse_mat) - assert np.allclose(sparse_mat.toarray(), true_matrix) - - @pytest.mark.parametrize("ps,wire_order,true_matrix", tup_ps_mat) - def test_to_mat_buffer(self, ps, wire_order, true_matrix): - """Test that the intermediate matrices are added correctly once the maximum buffer - size is reached.""" - buffer_size = 2 ** len(wire_order) * 48 # Buffer size for 2 matrices - sparse_mat = ps.to_mat(wire_order, format="csr", buffer_size=buffer_size) - assert np.allclose(sparse_mat.toarray(), true_matrix) - def test_simplify(self): """Test that simplify removes terms in the PauliSentence with coefficient less than the threshold""" @@ -1208,6 +1121,171 @@ def test_map_wires(self): ) +class TestPauliSentenceMatrix: + """Tests for calculating the matrix of a pauli sentence.""" + + PS_EMPTY_CASES = ( + (PauliSentence({}), np.zeros((1, 1))), + (PauliSentence({PauliWord({}): 1.0}), np.ones((1, 1))), + (PauliSentence({PauliWord({}): 2.5}), 2.5 * np.ones((1, 1))), + ( + PauliSentence({PauliWord({}): 2.5, PauliWord({0: "X"}): 1.0}), + 2.5 * np.eye(2) + qml.matrix(qml.PauliX(0)), + ), + ) + + @pytest.mark.parametrize("ps, true_res", PS_EMPTY_CASES) + def test_to_mat_empty(self, ps, true_res): + """Test that empty PauliSentences and PauliSentences with empty PauliWords are handled correctly""" + + res_dense = ps.to_mat() + assert np.allclose(res_dense, true_res) + res_sparse = ps.to_mat(format="csr") + assert sparse.issparse(res_sparse) + assert qml.math.allclose(res_sparse.todense(), true_res) + + def test_empty_pauli_to_mat_with_wire_order(self): + """Test the to_mat method with an empty PauliSentence and PauliWord and an external wire order.""" + actual = PauliSentence({PauliWord({}): 1.5}).to_mat([0, 1]) + assert np.allclose(actual, 1.5 * np.eye(4)) + + ps_wire_order = ((ps1, []), (ps1, [0, 1, 2, "a", "b"]), (ps3, [0, 1, "c"])) + + @pytest.mark.parametrize("ps, wire_order", ps_wire_order) + def test_to_mat_error_incomplete(self, ps, wire_order): + """Test that an appropriate error is raised when the wire order does + not contain all the PauliSentence's wires.""" + match = "Can't get the matrix for the specified wire order" + with pytest.raises(ValueError, match=match): + ps.to_mat(wire_order=wire_order) + + def test_to_mat_empty_sentence_with_wires(self): + """Test that a zero matrix is returned if wire_order is provided on an empty PauliSentence.""" + true_res = np.zeros((4, 4)) + res_dense = ps5.to_mat(wire_order=[0, 1]) + assert np.allclose(res_dense, true_res) + res_sparse = ps5.to_mat(wire_order=[0, 1], format="csr") + assert sparse.issparse(res_sparse) + assert qml.math.allclose(res_sparse.todense(), true_res) + + tup_ps_mat = ( + ( + ps1, + [0, 1, 2, "a", "b", "c"], + 1.23 * np.kron(np.kron(matI, np.kron(matX, matY)), np.eye(8)) + + 4j * np.kron(np.eye(8), np.kron(matX, np.kron(matX, matZ))) + - 0.5 * np.kron(matZ, np.kron(np.eye(8), np.kron(matZ, matZ))), + ), + ( + ps2, + ["a", "b", "c", 0, 1, 2], + -1.23 * np.kron(np.eye(8), np.kron(matI, np.kron(matX, matY))) + - 4j * np.kron(np.kron(matX, np.kron(matX, matZ)), np.eye(8)) + + 0.5 * np.kron(np.kron(matI, np.kron(matZ, np.kron(matZ, matZ))), np.eye(4)), + ), + ( + ps3, + [0, "b", "c"], + -0.5 * np.kron(matZ, np.kron(matZ, matZ)) + 1 * np.eye(8), + ), + ) + + @pytest.mark.parametrize("ps, wire_order, true_matrix", tup_ps_mat) + def test_to_mat_wire_order(self, ps, wire_order, true_matrix): + """Test that the wire_order is correctly incorporated in computing the + matrix representation.""" + assert np.allclose(ps.to_mat(wire_order), true_matrix) + + @pytest.mark.parametrize("ps, wire_order, true_matrix", tup_ps_mat) + def test_to_mat_format(self, ps, wire_order, true_matrix): + """Test that the correct type of matrix is returned given the format kwarg.""" + sparse_mat = ps.to_mat(wire_order, format="csr") + assert sparse.issparse(sparse_mat) + assert np.allclose(sparse_mat.toarray(), true_matrix) + + @pytest.mark.parametrize("ps,wire_order,true_matrix", tup_ps_mat) + def test_to_mat_buffer(self, ps, wire_order, true_matrix): + """Test that the intermediate matrices are added correctly once the maximum buffer + size is reached.""" + buffer_size = 2 ** len(wire_order) * 48 # Buffer size for 2 matrices + sparse_mat = ps.to_mat(wire_order, format="csr", buffer_size=buffer_size) + assert np.allclose(sparse_mat.toarray(), true_matrix) + + @pytest.mark.tf + def test_dense_matrix_tf(self): + """Test calculating the matrix for a pauli sentence is differentaible with tensorflow.""" + import tensorflow as tf + + x = tf.Variable(0.1 + 0j) + y = tf.Variable(0.2 + 0j) + + with tf.GradientTape() as tape: + _pw1 = PauliWord({0: "X", 1: "Y"}) + _pw2 = PauliWord({0: "Y", 1: "X"}) + H = x * _pw1 + y * _pw2 + mat = H.to_mat() + + gx, gy = tape.jacobian(mat, [x, y]) + + pw1_mat = np.array([[0, 0, 0, -1j], [0, 0, 1j, 0], [0, -1j, 0, 0], [1j, 0, 0, 0]]) + pw2_mat = np.array([[0, 0, 0, -1j], [0, 0, -1j, 0], [0, 1j, 0, 0], [1j, 0, 0, 0]]) + + assert qml.math.allclose(mat, x * pw1_mat + y * pw2_mat) + assert qml.math.allclose(gx, qml.math.conj(pw1_mat)) # tf complex number convention + assert qml.math.allclose(gy, qml.math.conj(pw2_mat)) + + @pytest.mark.torch + def test_dense_matrix_torch(self): + """Test calculating and differentiating the matrix with torch.""" + + import torch + + _pw1 = qml.pauli.PauliWord({0: "X", 1: "Z"}) + _pw2 = qml.pauli.PauliWord({0: "X"}) + + pw1_mat = torch.tensor([[0, 0, 1, 0], [0, 0, 0, -1], [1, 0, 0, 0], [0, -1, 0, 0]]) + pw2_mat = torch.tensor([[0, 0, 1, 0], [0, 0, 0, 1], [1, 0, 0, 0], [0, 1, 0, 0]]) + + x = torch.tensor(0.1, requires_grad=True) + y = torch.tensor(0.2, requires_grad=True) + + def f(x, y): + H = x * _pw1 + y * _pw2 + return qml.math.real(H.to_mat()) + + mat = f(x, y) + assert qml.math.allclose(mat, x * pw1_mat + y * pw2_mat) + + gx, gy = torch.autograd.functional.jacobian(f, (x, y)) + assert qml.math.allclose(gx, pw1_mat) + assert qml.math.allclose(gy, pw2_mat) + + @pytest.mark.jax + def test_dense_matrix_jax(self): + """Test calculating and differentiating the matrix with jax.""" + + import jax + + def f(x, y): + _pw1 = qml.pauli.PauliWord({0: "X", 1: "Y"}) + _pw2 = qml.pauli.PauliWord({0: "Y", 1: "X"}) + H = x * _pw1 + y * _pw2 + return H.to_mat() + + x = jax.numpy.array(0.1 + 0j) + y = jax.numpy.array(0.2 + 0j) + + pw1_mat = np.array([[0, 0, 0, -1j], [0, 0, 1j, 0], [0, -1j, 0, 0], [1j, 0, 0, 0]]) + pw2_mat = np.array([[0, 0, 0, -1j], [0, 0, -1j, 0], [0, 1j, 0, 0], [1j, 0, 0, 0]]) + + mat = f(x, y) + assert qml.math.allclose(mat, x * pw1_mat + y * pw2_mat) + + gx, gy = jax.jacobian(f, holomorphic=True, argnums=(0, 1))(x, y) + assert qml.math.allclose(gx, pw1_mat) + assert qml.math.allclose(gy, pw2_mat) + + class TestPaulicomms: """Test 'native' commutator in PauliWord and PauliSentence""" From d48fe9b0d2742b9d3369dd38e43d5a83a7c0510c Mon Sep 17 00:00:00 2001 From: albi3ro Date: Fri, 26 Apr 2024 14:51:17 -0400 Subject: [PATCH 08/10] changelog --- doc/releases/changelog-dev.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index 4d84dddca9f..9718d4a59f0 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -347,6 +347,10 @@

Other improvements

+* Calculating the dense, differentiable matrix for `PauliSentence` and operators with pauli sentences + is now faster. + [(#5578)](https://github.com/PennyLaneAI/pennylane/pull/5578) + * `qml.draw` and `qml.draw_mpl` will now attempt to sort the wires if no wire order is provided by the user or the device. [(#5576)](https://github.com/PennyLaneAI/pennylane/pull/5576) From 2eeda0984aeeb38102ddc31ebb2f428b6a301ebb Mon Sep 17 00:00:00 2001 From: Christina Lee Date: Fri, 26 Apr 2024 16:28:16 -0400 Subject: [PATCH 09/10] Apply suggestions from code review --- pennylane/pauli/pauli_arithmetic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pennylane/pauli/pauli_arithmetic.py b/pennylane/pauli/pauli_arithmetic.py index 2af858707da..9b253365f22 100644 --- a/pennylane/pauli/pauli_arithmetic.py +++ b/pennylane/pauli/pauli_arithmetic.py @@ -971,7 +971,7 @@ def _sum_same_structure_pws_dense(self, pauli_words, wire_order): ml_interface = qml.math.get_interface(coeff) if ml_interface == "torch": data0 = qml.math.convert_like(data0, coeff) - elif ml_interface == "tf": + elif ml_interface == "tensorflow": data0 = qml.math.cast_like(data0, coeff) data = coeff * data0 for pw in pauli_words[1:]: @@ -980,7 +980,7 @@ def _sum_same_structure_pws_dense(self, pauli_words, wire_order): ml_interface = qml.math.get_interface(coeff) if ml_interface == "torch": csr_data = qml.math.convert_like(csr_data, coeff) - elif ml_interface == "tf": + elif ml_interface == "tensorflow": csr_data = qml.math.cast_like(csr_data, coeff) data += self[pw] * csr_data From 623e9a5b88e8b75b8c56a45abfc9a627bbb25fc7 Mon Sep 17 00:00:00 2001 From: albi3ro Date: Fri, 26 Apr 2024 16:38:38 -0400 Subject: [PATCH 10/10] actually we might be able to just remove the tf casting --- pennylane/pauli/pauli_arithmetic.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pennylane/pauli/pauli_arithmetic.py b/pennylane/pauli/pauli_arithmetic.py index 2af858707da..9878b744b37 100644 --- a/pennylane/pauli/pauli_arithmetic.py +++ b/pennylane/pauli/pauli_arithmetic.py @@ -971,8 +971,6 @@ def _sum_same_structure_pws_dense(self, pauli_words, wire_order): ml_interface = qml.math.get_interface(coeff) if ml_interface == "torch": data0 = qml.math.convert_like(data0, coeff) - elif ml_interface == "tf": - data0 = qml.math.cast_like(data0, coeff) data = coeff * data0 for pw in pauli_words[1:]: coeff = self[pw] @@ -980,8 +978,6 @@ def _sum_same_structure_pws_dense(self, pauli_words, wire_order): ml_interface = qml.math.get_interface(coeff) if ml_interface == "torch": csr_data = qml.math.convert_like(csr_data, coeff) - elif ml_interface == "tf": - csr_data = qml.math.cast_like(csr_data, coeff) data += self[pw] * csr_data return qml.math.einsum("ij,i->ij", base_matrix, data)