From 9fdd4c46384e12a8f5ef32b37b365e3a961fc56a Mon Sep 17 00:00:00 2001 From: albi3ro Date: Fri, 2 Feb 2024 18:14:44 -0500 Subject: [PATCH 001/428] lightning interface for new device api --- .../lightning_qubit/lightning_qubit2.py | 354 ++++++++++++++++++ 1 file changed, 354 insertions(+) create mode 100644 pennylane_lightning/lightning_qubit/lightning_qubit2.py diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py new file mode 100644 index 0000000000..30cabef7df --- /dev/null +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -0,0 +1,354 @@ +# Copyright 2018-2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This module contains the LightningQubit2 class that inherits from the new device interface. + +""" +from typing import Union, Sequence, Optional +from dataclasses import replace +import numpy as np + +import pennylane as qml +from pennylane.devices import Device, ExecutionConfig, DefaultExecutionConfig +from pennylane.devices.preprocess import decompose, validate_device_wires, decompose, validate_measurements, validate_observables, no_sampling +from pennylane.devices.qubit.sampling import get_num_shots_and_executions +from pennylane.tape import QuantumScript +from pennylane.transforms.core import TransformProgram +from pennylane.typing import Result, ResultBatch + + +def dummy_simulate(circuit, rng=None, c_dtype=np.complex128, batch_obs=False, mcmc=False, + kernel_name="Local", num_burnin=100): + return tuple(0.0 for _ in circuit.measurements) + +def dummy_jacobian(circuit): + return np.array(0.0) + +def dummy_simulate_and_jacobian(circuit): + return np.array(0.0), np.array(0.0) + +Result_or_ResultBatch = Union[Result, ResultBatch] +QuantumTapeBatch = Sequence[qml.tape.QuantumTape] +QuantumTape_or_Batch = Union[qml.tape.QuantumTape, QuantumTapeBatch] + + +_operations = frozenset({ + "Identity", + "BasisState", + "QubitStateVector", + "StatePrep", + "QubitUnitary", + "ControlledQubitUnitary", + "MultiControlledX", + "DiagonalQubitUnitary", + "PauliX", + "PauliY", + "PauliZ", + "MultiRZ", + "Hadamard", + "S", + "Adjoint(S)", + "T", + "Adjoint(T)", + "SX", + "Adjoint(SX)", + "CNOT", + "SWAP", + "ISWAP", + "PSWAP", + "Adjoint(ISWAP)", + "SISWAP", + "Adjoint(SISWAP)", + "SQISW", + "CSWAP", + "Toffoli", + "CY", + "CZ", + "PhaseShift", + "ControlledPhaseShift", + "CPhase", + "RX", + "RY", + "RZ", + "Rot", + "CRX", + "CRY", + "CRZ", + "C(PauliX)", + "C(PauliY)", + "C(PauliZ)", + "C(Hadamard)", + "C(S)", + "C(T)", + "C(PhaseShift)", + "C(RX)", + "C(RY)", + "C(RZ)", + "C(SWAP)", + "C(IsingXX)", + "C(IsingXY)", + "C(IsingYY)", + "C(IsingZZ)", + "C(SingleExcitation)", + "C(SingleExcitationMinus)", + "C(SingleExcitationPlus)", + "C(DoubleExcitation)", + "C(DoubleExcitationMinus)", + "C(DoubleExcitationPlus)", + "CRot", + "IsingXX", + "IsingYY", + "IsingZZ", + "IsingXY", + "SingleExcitation", + "SingleExcitationPlus", + "SingleExcitationMinus", + "DoubleExcitation", + "DoubleExcitationPlus", + "DoubleExcitationMinus", + "QubitCarry", + "QubitSum", + "OrbitalRotation", + "QFT", + "ECR", + "BlockEncode", +}) + +_observables = frozenset({ + "PauliX", + "PauliY", + "PauliZ", + "Hadamard", + "Hermitian", + "Identity", + "Projector", + "SparseHamiltonian", + "Hamiltonian", + "Sum", + "SProd", + "Prod", + "Exp", +}) + +def stopping_condition(op: qml.operation.Operator) -> bool: + return op.name in _operations + +def accepted_observables(obs: qml.operation.Operator) -> bool: + return obs.name in _observables + +def accepted_measurements(m : qml.measurements.MeasurementProcess) -> bool: + return isinstance(m, (qml.measurements.ExpectationMP)) + +class LightningQubit2(Device): + """PennyLane Lightning Qubit device. + + A device that interfaces with C++ to perform fast linear algebra calculations. + + Use of this device requires pre-built binaries or compilation from source. Check out the + :doc:`/lightning_qubit/installation` guide for more details. + + Args: + wires (int): the number of wires to initialize the device with + c_dtype: Datatypes for statevector representation. Must be one of + ``np.complex64`` or ``np.complex128``. + shots (int): How many times the circuit should be evaluated (or sampled) to estimate + the expectation values. Defaults to ``None`` if not specified. Setting + to ``None`` results in computing statistics like expectation values and + variances analytically. + seed (str, int, rng) + mcmc (bool): Determine whether to use the approximate Markov Chain Monte Carlo + sampling method when generating samples. + kernel_name (str): name of transition kernel. The current version supports + two kernels: ``"Local"`` and ``"NonZeroRandom"``. + The local kernel conducts a bit-flip local transition between states. + The local kernel generates a random qubit site and then generates a random + number to determine the new bit at that qubit site. The ``"NonZeroRandom"`` kernel + randomly transits between states that have nonzero probability. + num_burnin (int): number of steps that will be dropped. Increasing this value will + result in a closer approximation but increased runtime. + batch_obs (bool): Determine whether we process observables in parallel when + computing the jacobian. This value is only relevant when the lightning + qubit is built with OpenMP. + """ + + name = 'lightning.qubit2' + + _device_options = ["rng", "c_dtype", "batch_obs", "mcmc", "kernel_name", "num_burnin"] + + def __init__( # pylint: disable=too-many-arguments + self, + wires=None, + *, + c_dtype=np.complex128, + shots=None, + seed="global", + mcmc=False, + kernel_name="Local", + num_burnin=100, + batch_obs=False, + ): + super().__init__(wires=wires, shots=shots) + seed = np.random.randint(0, high=10000000) if seed == "global" else seed + self._rng = np.random.default_rng(seed) + + self._c_dtype = c_dtype + self._batch_obs = batch_obs + self._mcmc = mcmc + if self._mcmc: + if kernel_name not in [ + "Local", + "NonZeroRandom", + ]: + raise NotImplementedError( + f"The {kernel_name} is not supported and currently " + "only 'Local' and 'NonZeroRandom' kernels are supported." + ) + if num_burnin >= shots: + raise ValueError("Shots should be greater than num_burnin.") + self._kernel_name = kernel_name + self._num_burnin = num_burnin + else: + self._kernel_name = None + self._num_burnin = None + + @property + def operation(self) -> frozenset[str]: + """The names of supported operations. + """ + return _operations + + @property + def observables(self) -> frozenset[str]: + """The names of supported observables. + """ + return _observables + + + def _setup_execution_config(self, config): + updated_values = {} + if config.gradient_method == "best": + updated_values["gradient_method"] = "adjoint" + if config.use_device_gradient is None: + updated_values["use_device_gradient"] = config.gradient_method in ("best", "adjoint") + if config.grad_on_execution is None: + updated_values["grad_on_execution"] = True + + new_device_options = dict(config.device_options) + for option in self._device_options: + if option not in new_device_options: + new_device_options[option] = getattr(self, f"_{option}", None) + + return replace(config, **updated_values, device_options=new_device_options) + + + def supports_derivatives( + self, + execution_config: Optional[ExecutionConfig] = None, + circuit: Optional[qml.tape.QuantumTape] = None, + ) -> bool: + if execution_config is None and circuit is None: + return True + if execution_config.gradient_method not in {"adjoint", "best"}: + return False + if circuit is None: + return True + return all(isinstance(m, qml.measurements.ExpectationMP) for m in circuit.measurements) and not circuit.shots + + + def preprocess(self, execution_config: ExecutionConfig = DefaultExecutionConfig): + program = TransformProgram() + program.add_transform(validate_measurements, analytic_measurements=accepted_measurements, name=self.name) + program.add_transform(no_sampling) + program.add_transform(validate_observables, accepted_observables, name=self.name) + program.add_transform(validate_device_wires, self.wires, name=self.name) + program.add_transform(qml.defer_measurements, device=self) + program.add_transform(decompose, stopping_condition=stopping_condition, name=self.name) + program.add_transform(qml.transforms.broadcast_expand) + return program, self._setup_execution_config(execution_config) + + def _execute_tracking(self, circuits): + self.tracker.update(batches=1) + self.tracker.record() + for c in circuits: + qpu_executions, shots = get_num_shots_and_executions(c) + if c.shots: + self.tracker.update( + simulations=1, + executions=qpu_executions, + shots=shots, + ) + else: + self.tracker.update( + simulations=1, + executions=qpu_executions, + ) + self.tracker.record() + + def execute(self, circuits : QuantumTape_or_Batch, execution_config: ExecutionConfig = DefaultExecutionConfig) -> Result_or_ResultBatch: + is_single_circuit = False + if isinstance(circuits, QuantumScript): + is_single_circuit = True + circuits = (circuits,) + + if self.tracker.active: + self._execute_tracking(circuits) + + results = [] + for circuit in circuits: + results.append(dummy_simulate(circuit, **execution_config.device_options)) + + return results[0] if is_single_circuit else tuple(results) + + + def compute_derivatives( + self, + circuits: QuantumTape_or_Batch, + execution_config: ExecutionConfig = DefaultExecutionConfig, + ): + is_single_circuit = False + if isinstance(circuits, QuantumScript): + is_single_circuit = True + circuits = [circuits] + + if self.tracker.active: + self.tracker.update(derivative_batches=1, derivatives=len(circuits)) + self.tracker.record() + res = tuple(dummy_jacobian(circuit) for circuit in circuits) + + return res[0] if is_single_circuit else res + + + def execute_and_compute_derivatives( + self, + circuits: QuantumTape_or_Batch, + execution_config: ExecutionConfig = DefaultExecutionConfig, + ): + is_single_circuit = False + if isinstance(circuits, QuantumScript): + is_single_circuit = True + circuits = [circuits] + + if self.tracker.active: + for c in circuits: + self.tracker.update(resources=c.specs["resources"]) + self.tracker.update( + execute_and_derivative_batches=1, + executions=len(circuits), + derivatives=len(circuits), + ) + self.tracker.record() + + results = tuple(dummy_simulate_and_jacobian(c) for c in circuits) + results, jacs = tuple(zip(*results)) + return (results[0], jacs[0]) if is_single_circuit else (results, jacs) \ No newline at end of file From 50c3bae4b5d1a67bb97235684e8f2bf8057b6acf Mon Sep 17 00:00:00 2001 From: Dev version update bot Date: Fri, 2 Feb 2024 23:23:35 +0000 Subject: [PATCH 002/428] Auto update version --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index 2501bfbac7..87a919ae46 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.35.0-dev11" +__version__ = "0.35.0-dev12" From 6ac4b89d5cf7021b1dcf0bb759eee24d21864e39 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Tue, 13 Feb 2024 08:59:23 -0500 Subject: [PATCH 003/428] Merge Master --- .github/CHANGELOG.md | 9 + .../workflows/compat-check-latest-latest.yml | 2 +- .../workflows/compat-check-latest-stable.yml | 2 +- .../compat-check-release-release.yml | 2 +- .../workflows/compat-check-stable-latest.yml | 2 +- .../workflows/compat-check-stable-stable.yml | 2 +- ...{tests_gpu_cu11.yml => tests_gpu_cuda.yml} | 17 +- .github/workflows/tests_linux_x86_mpi_gpu.yml | 37 ++-- .github/workflows/tests_windows.yml | 205 ++++++++++-------- ...4_cu11.yml => wheel_linux_x86_64_cuda.yml} | 29 ++- README.rst | 8 +- bin/auditwheel | 20 +- doc/requirements.txt | 2 +- pennylane_lightning/core/lightning_base.py | 14 +- .../lightning_gpu/lightning_gpu.py | 2 +- requirements-dev.txt | 2 +- tests/test_adjoint_jacobian.py | 1 + tests/test_decomposition.py | 6 +- 18 files changed, 211 insertions(+), 151 deletions(-) rename .github/workflows/{tests_gpu_cu11.yml => tests_gpu_cuda.yml} (96%) rename .github/workflows/{wheel_linux_x86_64_cu11.yml => wheel_linux_x86_64_cuda.yml} (76%) diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index be57b94646..bc36e8a477 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -7,8 +7,14 @@ ### Breaking changes +* Migrate `lightning.gpu` to CUDA 12. + [(#606)](https://github.com/PennyLaneAI/pennylane-lightning/pull/606) + ### Improvements +* Lower the overheads of Windows CI tests. + [(#610)](https://github.com/PennyLaneAI/pennylane-lightning/pull/610) + * Decouple LightningQubit memory ownership from numpy and migrate it to Lightning-Qubit managed state-vector class. [(#601)](https://github.com/PennyLaneAI/pennylane-lightning/pull/601) @@ -40,6 +46,9 @@ ### Bug fixes +* Ensure the stopping condition decompositions are respected for larger templated QFT and Grover operators. + [(#609)](https://github.com/PennyLaneAI/pennylane-lightning/pull/609) + * Move concurrency group specifications from reusable Docker build workflow to the root workflows. [(#604)](https://github.com/PennyLaneAI/pennylane-lightning/pull/604) diff --git a/.github/workflows/compat-check-latest-latest.yml b/.github/workflows/compat-check-latest-latest.yml index 16a11078b1..63533e089d 100644 --- a/.github/workflows/compat-check-latest-latest.yml +++ b/.github/workflows/compat-check-latest-latest.yml @@ -20,7 +20,7 @@ jobs: pennylane-version: latest tests_lgpu_gpu: name: Lightning Compatibility test (tests_lgpu_gpu) - latest/latest - uses: ./.github/workflows/tests_gpu_cu11.yml + uses: ./.github/workflows/tests_gpu_cuda.yml with: lightning-version: latest pennylane-version: latest diff --git a/.github/workflows/compat-check-latest-stable.yml b/.github/workflows/compat-check-latest-stable.yml index d370a1240e..3e589283eb 100644 --- a/.github/workflows/compat-check-latest-stable.yml +++ b/.github/workflows/compat-check-latest-stable.yml @@ -20,7 +20,7 @@ jobs: pennylane-version: stable tests_lgpu_gpu: name: Lightning Compatibility test (tests_lgpu_gpu) - latest/stable - uses: ./.github/workflows/tests_gpu_cu11.yml + uses: ./.github/workflows/tests_gpu_cuda.yml with: lightning-version: latest pennylane-version: stable diff --git a/.github/workflows/compat-check-release-release.yml b/.github/workflows/compat-check-release-release.yml index 9c179708e9..52c6bea41c 100644 --- a/.github/workflows/compat-check-release-release.yml +++ b/.github/workflows/compat-check-release-release.yml @@ -20,7 +20,7 @@ jobs: pennylane-version: release tests_lgpu_gpu: name: Lightning Compatibility test (tests_lgpu_gpu) - release/release - uses: ./.github/workflows/tests_gpu_cu11.yml + uses: ./.github/workflows/tests_gpu_cuda.yml with: lightning-version: release pennylane-version: release diff --git a/.github/workflows/compat-check-stable-latest.yml b/.github/workflows/compat-check-stable-latest.yml index 65eceb4aed..f7e125ca69 100644 --- a/.github/workflows/compat-check-stable-latest.yml +++ b/.github/workflows/compat-check-stable-latest.yml @@ -20,7 +20,7 @@ jobs: pennylane-version: latest tests_lgpu_gpu: name: Lightning Compatibility test (tests_lgpu_gpu) - stable/latest - uses: ./.github/workflows/tests_gpu_cu11.yml + uses: ./.github/workflows/tests_gpu_cuda.yml with: lightning-version: stable pennylane-version: latest diff --git a/.github/workflows/compat-check-stable-stable.yml b/.github/workflows/compat-check-stable-stable.yml index 4bcba8fcbb..8f33068c39 100644 --- a/.github/workflows/compat-check-stable-stable.yml +++ b/.github/workflows/compat-check-stable-stable.yml @@ -20,7 +20,7 @@ jobs: pennylane-version: stable tests_lgpu_gpu: name: Lightning Compatibility test (tests_lgpu_gpu) - stable/stable - uses: ./.github/workflows/tests_gpu_cu11.yml + uses: ./.github/workflows/tests_gpu_cuda.yml with: lightning-version: stable pennylane-version: stable diff --git a/.github/workflows/tests_gpu_cu11.yml b/.github/workflows/tests_gpu_cuda.yml similarity index 96% rename from .github/workflows/tests_gpu_cu11.yml rename to .github/workflows/tests_gpu_cuda.yml index 12d4c26786..06741a1152 100644 --- a/.github/workflows/tests_gpu_cu11.yml +++ b/.github/workflows/tests_gpu_cuda.yml @@ -39,25 +39,27 @@ jobs: matrix: os: [ubuntu-22.04] pl_backend: ["lightning_gpu"] + cuda_version: ["12"] steps: - name: Validate GPU version and installed compiler run: | source /etc/profile.d/modules.sh module use /opt/modules - module load cuda/11.8 + module load cuda/${{ matrix.cuda_version }} echo "${PATH}" >> $GITHUB_PATH echo "LD_LIBRARY_PATH=${LD_LIBRARY_PATH}" >> $GITHUB_ENV nvcc --version nvidia-smi - cpptestswithLGPU_cu11: + cpptestswithLGPU: if: ${{ !contains(fromJSON('["schedule", "workflow_dispatch"]'), github.event_name) }} needs: [builddeps] strategy: matrix: os: [ubuntu-22.04] pl_backend: ["lightning_gpu"] + cuda_version: ["12"] name: C++ tests (Lightning-GPU) runs-on: @@ -70,7 +72,7 @@ jobs: run: | source /etc/profile.d/modules.sh module use /opt/modules - module load cuda/11.8 + module load cuda/${{ matrix.cuda_version }} echo "${PATH}" >> $GITHUB_PATH echo "LD_LIBRARY_PATH=${LD_LIBRARY_PATH}" >> $GITHUB_ENV nvcc --version @@ -117,7 +119,7 @@ jobs: - name: Install required packages run: | - python -m pip install ninja cmake custatevec-cu11 + python -m pip install ninja cmake custatevec-cu${{ matrix.cuda_version }} sudo apt-get -y -q install liblapack-dev - name: Build and run unit tests @@ -161,6 +163,7 @@ jobs: os: [ubuntu-22.04] pl_backend: ["lightning_gpu"] default_backend: ["lightning_qubit"] + cuda_version: ["12"] name: Python tests with LGPU runs-on: @@ -173,7 +176,7 @@ jobs: run: | source /etc/profile.d/modules.sh module use /opt/modules - module load cuda/11.8 + module load cuda/${{ matrix.cuda_version }} echo "${PATH}" >> $GITHUB_PATH echo "LD_LIBRARY_PATH=${LD_LIBRARY_PATH}" >> $GITHUB_ENV nvcc --version @@ -238,7 +241,7 @@ jobs: run: | cd main python -m pip install -r requirements-dev.txt - python -m pip install cmake custatevec-cu11 openfermionpyscf + python -m pip install cmake custatevec-cu${{ matrix.cuda_version }} openfermionpyscf - name: Checkout PennyLane for release build if: inputs.pennylane-version == 'release' @@ -344,7 +347,7 @@ jobs: token: ${{ secrets.CODECOV_TOKEN }} upload-to-codecov-linux-cpp: - needs: [cpptestswithLGPU_cu11] + needs: [cpptestswithLGPU] name: Upload coverage data to codecov runs-on: ubuntu-latest steps: diff --git a/.github/workflows/tests_linux_x86_mpi_gpu.yml b/.github/workflows/tests_linux_x86_mpi_gpu.yml index 648e68bdb3..7af97b5046 100644 --- a/.github/workflows/tests_linux_x86_mpi_gpu.yml +++ b/.github/workflows/tests_linux_x86_mpi_gpu.yml @@ -39,6 +39,8 @@ jobs: max-parallel: 1 matrix: mpilib: ["mpich", "openmpi"] + cuda_version_maj: ["12"] + cuda_version_min: ["2"] timeout-minutes: 30 steps: @@ -92,28 +94,29 @@ jobs: - name: Install required packages run: | python -m pip install -r requirements-dev.txt - python -m pip install cmake custatevec-cu11 + python -m pip install cmake custatevec-cu12 sudo apt-get -y -q install liblapack-dev - - name: Validate GPU version and installed compiler + - name: Validate GPU version and installed compiler and modules run: | - source /etc/profile.d/modules.sh && module use /opt/modules && module load cuda/11.8 + source /etc/profile.d/modules.sh && module use /opt/modules && module load cuda/${{ matrix.cuda_version_maj }} which -a nvcc nvcc --version + ls -R /opt/modules - name: Validate Multi-GPU packages run: | - source /etc/profile.d/modules.sh && module use /opt/modules/ && module load ${{ matrix.mpilib }} + source /etc/profile.d/modules.sh && module use /opt/modules/ && module load ${{ matrix.mpilib }}/cuda-${{ matrix.cuda_version_maj }}.${{ matrix.cuda_version_min }} echo 'Checking for ${{ matrix.mpilib }}' which -a mpirun mpirun --version which -a mpicxx mpicxx --version - module unload ${{ matrix.mpilib }} + module unload ${{ matrix.mpilib }}/cuda-${{ matrix.cuda_version_maj }}.${{ matrix.cuda_version_min }} - name: Build and run unit tests run: | - source /etc/profile.d/modules.sh && module use /opt/modules/ && module load ${{ matrix.mpilib }} + source /etc/profile.d/modules.sh && module use /opt/modules/ && module load ${{ matrix.mpilib }}/cuda-${{ matrix.cuda_version_maj }}.${{ matrix.cuda_version_min }} export CUQUANTUM_SDK=$(python -c "import site; print( f'{site.getsitepackages()[0]}/cuquantum/lib')") cmake . -BBuild \ -DPL_BACKEND=lightning_gpu \ @@ -123,7 +126,7 @@ jobs: -DBUILD_TESTS=ON \ -DENABLE_LAPACK=ON \ -DCMAKE_CXX_COMPILER=mpicxx \ - -DCMAKE_CUDA_COMPILER="/usr/local/cuda/bin/nvcc" \ + -DCMAKE_CUDA_COMPILER=$(which nvcc) \ -DCMAKE_CUDA_ARCHITECTURES="86" \ -DPython_EXECUTABLE:FILE="${{ steps.python_path.outputs.python }}" \ -G Ninja @@ -131,7 +134,7 @@ jobs: cd ./Build mkdir -p ./tests/results for file in *runner ; do ./$file --order lex --reporter junit --out ./tests/results/report_$file.xml; done; - for file in *runner_mpi ; do /opt/mpi/${{ matrix.mpilib }}/bin/mpirun -np 2 ./$file --order lex --reporter junit --out ./tests/results/report_$file.xml; done; + for file in *runner_mpi ; do mpirun -np 2 ./$file --order lex --reporter junit --out ./tests/results/report_$file.xml; done; lcov --directory . -b ../pennylane_lightning/src --capture --output-file coverage.info lcov --remove coverage.info '/usr/*' --output-file coverage.info mv coverage.info coverage-${{ github.job }}-lightning_gpu_${{ matrix.mpilib }}.info @@ -171,6 +174,8 @@ jobs: max-parallel: 1 matrix: mpilib: ["mpich", "openmpi"] + cuda_version_maj: ["12"] + cuda_version_min: ["2"] timeout-minutes: 30 steps: @@ -232,9 +237,9 @@ jobs: - name: Install required packages run: | - source /etc/profile.d/modules.sh && module use /opt/modules/ && module load ${{ matrix.mpilib }} + source /etc/profile.d/modules.sh && module use /opt/modules/ && module load ${{ matrix.mpilib }}/cuda-${{ matrix.cuda_version_maj }}.${{ matrix.cuda_version_min }} python -m pip install -r requirements-dev.txt - python -m pip install custatevec-cu11 mpi4py openfermionpyscf + python -m pip install custatevec-cu${{ matrix.cuda_version_maj }} mpi4py openfermionpyscf SKIP_COMPILATION=True PL_BACKEND=lightning_qubit python -m pip install -e . -vv - name: Checkout PennyLane for release build @@ -256,25 +261,25 @@ jobs: env: CUQUANTUM_SDK: $(python -c "import site; print( f'{site.getsitepackages()[0]}/cuquantum/lib')") run: | - source /etc/profile.d/modules.sh && module use /opt/modules/ && module load ${{ matrix.mpilib }} - CMAKE_ARGS="-DCMAKE_C_COMPILER=mpicc -DCMAKE_CXX_COMPILER=mpicxx -DENABLE_MPI=ON -DCMAKE_CUDA_COMPILER=/usr/local/cuda/bin/nvcc -DCMAKE_CUDA_ARCHITECTURES=${{ env.CI_CUDA_ARCH }} -DPython_EXECUTABLE=${{ steps.python_path.outputs.python }}" \ + source /etc/profile.d/modules.sh && module use /opt/modules/ && module load ${{ matrix.mpilib }}/cuda-${{ matrix.cuda_version_maj }}.${{ matrix.cuda_version_min }} + CMAKE_ARGS="-DCMAKE_C_COMPILER=mpicc -DCMAKE_CXX_COMPILER=mpicxx -DENABLE_MPI=ON -DCMAKE_CUDA_COMPILER=$(which nvcc) -DCMAKE_CUDA_ARCHITECTURES=${{ env.CI_CUDA_ARCH }} -DPython_EXECUTABLE=${{ steps.python_path.outputs.python }}" \ PL_BACKEND=lightning_gpu python -m pip install -e . --verbose # There are issues running py-cov with MPI. A solution is to use coverage as reported # [here](https://github.com/pytest-dev/pytest-cov/issues/237#issuecomment-544824228) - name: Run unit tests for MPI-enabled lightning.gpu device run: | - source /etc/profile.d/modules.sh && module use /opt/modules/ && module load ${{ matrix.mpilib }} - PL_DEVICE=lightning.gpu /opt/mpi/${{ matrix.mpilib }}/bin/mpirun -np 2 \ + source /etc/profile.d/modules.sh && module use /opt/modules/ && module load ${{ matrix.mpilib }}/cuda-${{ matrix.cuda_version_maj }}.${{ matrix.cuda_version_min }} + PL_DEVICE=lightning.gpu mpirun -np 2 \ coverage run --rcfile=.coveragerc --source=pennylane_lightning -p -m mpi4py -m pytest ./mpitests --tb=native coverage combine - coverage xml -o coverage-${{ github.job }}-lightning_gpu_${{ matrix.mpilib }}-main.xml + coverage xml -o coverage-${{ github.job }}-lightning_gpu_${{ matrix.mpilib }}_cu${{ matrix.cuda_version_maj }}-main.xml - name: Upload code coverage results uses: actions/upload-artifact@v3 with: name: ubuntu-codecov-results-python - path: coverage-${{ github.job }}-lightning_gpu_${{ matrix.mpilib }}-*.xml + path: coverage-${{ github.job }}-lightning_gpu_${{ matrix.mpilib }}_cu${{ matrix.cuda_version_maj }}-*.xml if-no-files-found: error - name: Cleanup diff --git a/.github/workflows/tests_windows.yml b/.github/workflows/tests_windows.yml index 1d56af45b5..395d4a8bdd 100644 --- a/.github/workflows/tests_windows.yml +++ b/.github/workflows/tests_windows.yml @@ -20,76 +20,6 @@ concurrency: cancel-in-progress: true jobs: - cpptests: - if: ${{ !contains(fromJSON('["schedule", "workflow_dispatch"]'), github.event_name) }} - timeout-minutes: 45 - name: C++ tests (Windows) - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [windows-latest] - pl_backend: ["lightning_qubit"] - steps: - - name: Checkout PennyLane-Lightning - uses: actions/checkout@v3 - - - name: Configure MSVC for amd64 # Use cl.exe as a default compiler - uses: ilammy/msvc-dev-cmd@v1 - with: - arch: amd64 - - - name: Install lapack with vcpkg - run: | - git clone --quiet --recursive https://github.com/microsoft/vcpkg.git - cd vcpkg - .\bootstrap-vcpkg.bat - vcpkg install lapack - - - name: Setup OpenCppCoverage and add to PATH - run: | - choco install OpenCppCoverage -y - echo "C:\Program Files\OpenCppCoverage" >> $env:GITHUB_PATH - - - name: Build and run unit tests for code coverage - run: | - cmake -BBuild ` - -DBUILD_TESTS=ON ` - -DENABLE_OPENMP=OFF ` - -DENABLE_PYTHON=OFF ` - -DENABLE_GATE_DISPATCHER=OFF ` - -DPL_BACKEND=${{ matrix.pl_backend }} ` - -DENABLE_LAPACK=ON ` - -DCMAKE_TOOLCHAIN_FILE=D:/a/pennylane-lightning/pennylane-lightning/vcpkg/scripts/buildsystems/vcpkg.cmake ` - -DENABLE_WARNINGS=OFF - cmake --build .\Build --config Debug - mkdir -p .\Build\tests\results - $test_bins = Get-ChildItem -Include *.exe -Recurse -Path ./Build/Debug - foreach ($file in $test_bins) - { - $filename = $file.ToString() -replace '.{4}$' - $filename = $filename.Substring($filename.LastIndexOf("\")+1) - $test_call = $file.ToString() + " --order lex --reporter junit --out .\Build\tests\results\report_" + $filename + ".xml" - Invoke-Expression $test_call - $cov_call = "OpenCppCoverage --sources pennylane_lightning\core\src --export_type cobertura:coverage.xml " + $file.ToString() - Invoke-Expression $cov_call - } - Move-Item -Path .\coverage.xml -Destination .\coverage-${{ github.job }}-${{ matrix.pl_backend }}.xml - - - name: Upload test results - uses: actions/upload-artifact@v3 - if: always() - with: - name: windows-test-report-${{ github.job }}-${{ matrix.pl_backend }} - path: .\Build\tests\results\ - if-no-files-found: error - - - name: Upload coverage results - uses: actions/upload-artifact@v3 - with: - name: windows-coverage-report - path: .\coverage-${{ github.job }}-${{ matrix.pl_backend }}.xml - if-no-files-found: error - win-set-matrix-x86: name: Set builder matrix runs-on: ubuntu-latest @@ -110,7 +40,7 @@ jobs: exec_model: ${{ steps.exec_model.outputs.exec_model }} kokkos_version: ${{ steps.kokkos_version.outputs.kokkos_version }} - build_dependencies: + build_dependencies_kokkos: needs: [win-set-matrix-x86] strategy: fail-fast: false @@ -125,7 +55,7 @@ jobs: steps: - name: Cache installation directories id: kokkos-cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: D:\a\install_dir\${{ matrix.exec_model }} key: ${{ matrix.os }}-kokkos${{ matrix.kokkos_version }}-${{ matrix.exec_model }}-Debug @@ -163,9 +93,111 @@ jobs: cmake --build ./Build --config Debug --verbose cmake --install ./Build --config Debug --verbose + build_dependencies_vcpkg: + strategy: + fail-fast: false + matrix: + os: [windows-latest] + timeout-minutes: 30 + name: vcpkg dependencies + runs-on: ${{ matrix.os }} + + steps: + - name: Cache installation directories + id: vcpkg-cache + uses: actions/cache@v4 + with: + path: D:\a\install_dir\vcpkg + key: ${{ matrix.os }}-vcpkg + + - name: Clone vcpkg + if: steps.vcpkg-cache.outputs.cache-hit != 'true' + run: | + mkdir -p D:\a\install_dir + cd D:\a\install_dir\ + git clone --quiet --recursive https://github.com/microsoft/vcpkg.git + + - name: Install dependencies + if: steps.vcpkg-cache.outputs.cache-hit != 'true' + run: | + python -m pip install cmake build ninja + + - name: Build Lapack library + if: steps.vcpkg-cache.outputs.cache-hit != 'true' + run: | + cd D:\a\install_dir\vcpkg + .\bootstrap-vcpkg.bat + vcpkg install lapack + + cpptests: + needs: [build_dependencies_vcpkg] + if: ${{ !contains(fromJSON('["schedule", "workflow_dispatch"]'), github.event_name) }} + timeout-minutes: 30 + name: C++ tests (Windows) + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [windows-latest] + pl_backend: ["lightning_qubit"] + steps: + - name: Checkout PennyLane-Lightning + uses: actions/checkout@v3 + + - name: Restoring cached vcpkg + id: vcpkg-cache + uses: actions/cache@v4 + with: + path: D:\a\install_dir\vcpkg + key: ${{ matrix.os }}-vcpkg + + - name: Setup OpenCppCoverage and add to PATH + run: | + choco install OpenCppCoverage -y + echo "C:\Program Files\OpenCppCoverage" >> $env:GITHUB_PATH + + - name: Build and run unit tests for code coverage + run: | + cmake -BBuild ` + -DBUILD_TESTS=ON ` + -DENABLE_OPENMP=OFF ` + -DENABLE_PYTHON=OFF ` + -DENABLE_GATE_DISPATCHER=OFF ` + -DPL_BACKEND=${{ matrix.pl_backend }} ` + -DENABLE_LAPACK=ON ` + -DCMAKE_TOOLCHAIN_FILE=D:\a\install_dir\vcpkg\scripts\buildsystems\vcpkg.cmake ` + -DENABLE_WARNINGS=OFF + cmake --build .\Build --config RelWithDebInfo + mkdir -p .\Build\tests\results + $test_bins = Get-ChildItem -Include *.exe -Recurse -Path ./Build/RelWithDebInfo + foreach ($file in $test_bins) + { + $filename = $file.ToString() -replace '.{4}$' + $filename = $filename.Substring($filename.LastIndexOf("\")+1) + $test_call = $file.ToString() + " --order lex --reporter junit --out .\Build\tests\results\report_" + $filename + ".xml" + Invoke-Expression $test_call + $cov_call = "OpenCppCoverage --sources pennylane_lightning\core\src --excluded_modules D:\a\install_dir\* --excluded_modules C:\Windows\System32\* --export_type cobertura:coverage.xml " + $file.ToString() + Invoke-Expression $cov_call + } + Move-Item -Path .\coverage.xml -Destination .\coverage-${{ github.job }}-${{ matrix.pl_backend }}.xml + + - name: Upload test results + uses: actions/upload-artifact@v3 + if: always() + with: + name: windows-test-report-${{ github.job }}-${{ matrix.pl_backend }} + path: .\Build\tests\results\ + if-no-files-found: error + + - name: Upload coverage results + uses: actions/upload-artifact@v3 + with: + name: windows-coverage-report + path: .\coverage-${{ github.job }}-${{ matrix.pl_backend }}.xml + if-no-files-found: error + cpptestswithkokkos: if: ${{ !contains(fromJSON('["schedule", "workflow_dispatch"]'), github.event_name) }} - needs: [build_dependencies, win-set-matrix-x86] + needs: [build_dependencies_kokkos, build_dependencies_vcpkg, win-set-matrix-x86] strategy: matrix: os: [windows-latest] @@ -180,32 +212,27 @@ jobs: steps: - name: Restoring cached Kokkos id: kokkos-cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: D:\a\install_dir\${{ matrix.exec_model }} key: ${{ matrix.os }}-kokkos${{ matrix.kokkos_version }}-${{ matrix.exec_model }}-Debug + + - name: Restoring cached vcpkg + id: vcpkg-cache + uses: actions/cache@v4 + with: + path: D:\a\install_dir\vcpkg + key: ${{ matrix.os }}-vcpkg - name: Checkout PennyLane-Lightning uses: actions/checkout@v3 - - name: Copy cached libraries + - name: Copy cached Kokkos libraries if: steps.kokkos-cache.outputs.cache-hit == 'true' run: | Copy-Item -Path "D:\a\install_dir\${{ matrix.exec_model }}\" ` -Destination "D:\a\pennylane-lightning\pennylane-lightning\Kokkos" -Recurse -Force - - name: Configure MSVC for amd64 # Use cl.exe as a default compiler - uses: ilammy/msvc-dev-cmd@v1 - with: - arch: amd64 - - - name: Install lapack with vcpkg - run: | - git clone --quiet --recursive https://github.com/microsoft/vcpkg.git - cd vcpkg - .\bootstrap-vcpkg.bat - vcpkg install lapack - - name: Enable long paths run: | powershell.exe New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" -Name "LongPathsEnabled" -Value 1 -PropertyType DWORD -Force @@ -224,7 +251,7 @@ jobs: -DENABLE_PYTHON=OFF ` -DENABLE_GATE_DISPATCHER=OFF ` -DCMAKE_PREFIX_PATH=D:\a\pennylane-lightning\pennylane-lightning\Kokkos ` - -DCMAKE_TOOLCHAIN_FILE=D:/a/pennylane-lightning/pennylane-lightning/vcpkg/scripts/buildsystems/vcpkg.cmake ` + -DCMAKE_TOOLCHAIN_FILE=D:\a\install_dir\vcpkg\scripts\buildsystems\vcpkg.cmake ` -DENABLE_OPENMP=OFF ` -DPL_BACKEND=${{ matrix.pl_backend }} ` -DENABLE_LAPACK=ON ` @@ -238,7 +265,7 @@ jobs: $filename = $filename.Substring($filename.LastIndexOf("\")+1) $test_call = $file.ToString() + " --order lex --reporter junit --out .\Build\tests\results\report_" + $filename + ".xml" Invoke-Expression $test_call - $cov_call = "OpenCppCoverage --sources pennylane_lightning\core\src --export_type cobertura:coverage.xml " + $file.ToString() + $cov_call = "OpenCppCoverage --sources pennylane_lightning\core\src --excluded_modules D:\a\install_dir\* --excluded_modules C:\Windows\System32\* --export_type cobertura:coverage.xml " + $file.ToString() Invoke-Expression $cov_call } Move-Item -Path .\coverage.xml -Destination .\coverage-${{ github.job }}-${{ matrix.pl_backend }}.xml diff --git a/.github/workflows/wheel_linux_x86_64_cu11.yml b/.github/workflows/wheel_linux_x86_64_cuda.yml similarity index 76% rename from .github/workflows/wheel_linux_x86_64_cu11.yml rename to .github/workflows/wheel_linux_x86_64_cuda.yml index 33a5d6f8b3..27a1841a4e 100644 --- a/.github/workflows/wheel_linux_x86_64_cu11.yml +++ b/.github/workflows/wheel_linux_x86_64_cuda.yml @@ -1,4 +1,4 @@ -name: Wheel::Linux::x86_64::CUDA-11 +name: Wheel::Linux::x86_64::CUDA # **What it does**: Builds python wheels for Linux (ubuntu-latest) architecture x86_64 and store it as artifacts. # Python versions: 3.9, 3.10, 3.11, 3.12. @@ -6,9 +6,7 @@ name: Wheel::Linux::x86_64::CUDA-11 # **Who does it impact**: Wheels to be uploaded to PyPI. env: - GCC_VERSION: 11 - CUDA_VERSION_MAJOR: 11 - CUDA_VERSION_MINOR: 5 + GCC_VERSION: "11" on: pull_request: @@ -19,7 +17,7 @@ on: types: [published] concurrency: - group: wheel_linux_x86_64_cu11-${{ github.ref }} + group: wheel_linux_x86_64_cu12-${{ github.ref }} cancel-in-progress: true jobs: @@ -37,15 +35,15 @@ jobs: os: [ubuntu-latest] arch: [x86_64] pl_backend: ["lightning_gpu"] + cuda_version: ["12"] cibw_build: ${{ fromJson(needs.set_wheel_build_matrix.outputs.python_version) }} container_img: ["quay.io/pypa/manylinux2014_x86_64"] timeout-minutes: 30 - name: ${{ matrix.os }}::${{ matrix.arch }} - ${{ matrix.pl_backend }} (Python ${{ fromJson('{ "cp39-*":"3.9","cp310-*":"3.10","cp311-*":"3.11","cp312-*":"3.12" }')[matrix.cibw_build] }}) + name: ${{ matrix.os }}::${{ matrix.arch }} - ${{ matrix.pl_backend }} CUDA ${{ matrix.cuda_version }} (Python ${{ fromJson('{ "cp39-*":"3.9","cp310-*":"3.10","cp311-*":"3.11","cp312-*":"3.12" }')[matrix.cibw_build] }}) runs-on: ${{ matrix.os }} container: ${{ matrix.container_img }} steps: - - name: Checkout PennyLane-Lightning uses: actions/checkout@v3 @@ -67,24 +65,25 @@ jobs: CIBW_SKIP: "*-musllinux*" - CIBW_CONFIG_SETTINGS: --global-option=build_ext --global-option=--define="CMAKE_CXX_COMPILER=$(which g++-11);CMAKE_C_COMPILER=$(which gcc-11);LIGHTNING_RELEASE_TAG=master" + CIBW_CONFIG_SETTINGS: --global-option=build_ext --global-option=--define="CMAKE_CXX_COMPILER=$(which g++);CMAKE_C_COMPILER=$(which gcc);LIGHTNING_RELEASE_TAG=master" # Python build settings CIBW_BEFORE_BUILD: | cat /etc/yum.conf | sed "s/\[main\]/\[main\]\ntimeout=5/g" > /etc/yum.conf - python -m pip install ninja cmake~=3.24.3 auditwheel custatevec-cu11 + python -m pip install ninja cmake~=3.24.3 auditwheel custatevec-cu${{ matrix.cuda_version }} yum clean all -y yum install centos-release-scl-rh -y yum install devtoolset-11-gcc-c++ -y source /opt/rh/devtoolset-11/enable -y yum-config-manager --add-repo https://developer.download.nvidia.com/compute/cuda/repos/rhel7/x86_64/cuda-rhel7.repo -y - yum -y install cuda-11-5 git openssh wget + yum -y install cuda-${{ matrix.cuda_version }}-0 git openssh wget # ensure nvcc is available CIBW_ENVIRONMENT: | - PATH=$PATH:/usr/local/cuda/bin \ - LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/cuda/lib64 \ - PL_BACKEND="${{ matrix.pl_backend }}" + PATH=/opt/rh/devtoolset-11/root/usr/bin:$PATH:/usr/local/cuda-${{ matrix.cuda_version }}/bin \ + LD_LIBRARY_PATH=/opt/rh/devtoolset-11/root/usr/lib64:/opt/rh/devtoolset-11/root/usr/lib:/opt/rh/devtoolset-11/root/usr/lib64/dyninst:/opt/rh/devtoolset-11/root/usr/lib/dyninst:$LD_LIBRARY_PATH:/usr/local/cuda-${{ matrix.cuda_version }}/lib64 \ + PL_BACKEND="${{ matrix.pl_backend }}" \ + PKG_CONFIG_PATH=/opt/rh/devtoolset-11/root/usr/lib64/pkgconfig:$PKG_CONFIG_PATH CIBW_REPAIR_WHEEL_COMMAND_LINUX: "./bin/auditwheel repair -w {dest_dir} {wheel}" @@ -108,7 +107,7 @@ jobs: - uses: actions/upload-artifact@v3 if: ${{ github.event_name == 'release' || github.ref == 'refs/heads/master' || steps.rc_build.outputs.match != ''}} with: - name: ${{ runner.os }}-wheels-${{ matrix.pl_backend }}-${{ matrix.arch }}.zip + name: ${{ runner.os }}-wheels-${{ matrix.pl_backend }}-${{ matrix.arch }}-cu${{ matrix.cuda_version }} path: ./wheelhouse/*.whl upload-pypi: @@ -122,7 +121,7 @@ jobs: steps: - uses: actions/download-artifact@v3 with: - name: Linux-wheels-${{ matrix.pl_backend }}-${{ matrix.arch }}.zip + name: ${{ runner.os }}-wheels-${{ matrix.pl_backend }}-${{ matrix.arch }}-cu${{ matrix.cuda_version }} path: dist - name: Upload wheels to PyPI diff --git a/README.rst b/README.rst index f82b4282a6..74090c8266 100644 --- a/README.rst +++ b/README.rst @@ -121,7 +121,7 @@ On MacOS, we recommend using the latest version of ``clang++`` and ``libomp``: $ brew install llvm libomp -The Lightning-GPU backend has several dependencies (e.g. ``CUDA``, ``custatevec-cu11``, etc.), and hence we recommend referring to Lightning-GPU installation section. +The Lightning-GPU backend has several dependencies (e.g. ``CUDA``, ``custatevec-cu12``, etc.), and hence we recommend referring to Lightning-GPU installation section. Similarly, for Lightning-Kokkos it is recommended to configure and install Kokkos independently as prescribed in the Lightning-Kokkos installation section. Development installation @@ -227,7 +227,7 @@ Lightning-GPU can be installed using ``pip``: pip install pennylane-lightning[gpu] -Lightning-GPU requires the `cuQuantum SDK `_ (only the `cuStateVec `_ library is required). +Lightning-GPU requires CUDA 12 and the `cuQuantum SDK `_ (only the `cuStateVec `_ library is required). The SDK may be installed within the Python environment ``site-packages`` directory using ``pip`` or ``conda`` or the SDK library path appended to the ``LD_LIBRARY_PATH`` environment variable. Please see the `cuQuantum SDK`_ install guide for more information. @@ -247,7 +247,7 @@ Then the `cuStateVec`_ library can be installed and set a ``CUQUANTUM_SDK`` envi .. code-block:: console - python -m pip install wheel custatevec-cu11 + python -m pip install wheel custatevec-cu12 export CUQUANTUM_SDK=$(python -c "import site; print( f'{site.getsitepackages()[0]}/cuquantum/lib')") The Lightning-GPU can then be installed with ``pip``: @@ -261,7 +261,7 @@ To simplify the build, we recommend using the containerized build process descri Install Lightning-GPU with MPI ============================== -Building Lightning-GPU with MPI also requires the ``NVIDIA cuQuantum SDK`` (currently supported version: `custatevec-cu11 `_), ``mpi4py`` and ``CUDA-aware MPI`` (Message Passing Interface). +Building Lightning-GPU with MPI also requires the ``NVIDIA cuQuantum SDK`` (currently supported version: `custatevec-cu12 `_), ``mpi4py`` and ``CUDA-aware MPI`` (Message Passing Interface). ``CUDA-aware MPI`` allows data exchange between GPU memory spaces of different nodes without the need for CPU-mediated transfers. Both the ``MPICH`` and ``OpenMPI`` libraries are supported, provided they are compiled with CUDA support. The path to ``libmpi.so`` should be found in ``LD_LIBRARY_PATH``. diff --git a/bin/auditwheel b/bin/auditwheel index e7142fa4ab..84de9fa7e1 100755 --- a/bin/auditwheel +++ b/bin/auditwheel @@ -5,21 +5,35 @@ import sys from auditwheel.main import main -from auditwheel.policy import _POLICIES as POLICIES +from auditwheel.policy import WheelPolicies as WP # Do not include licensed dynamic libraries libs = [ "libcudart.so.11.0", + "libcudart.so.12.0", "libcublasLt.so.11", + "libcublasLt.so.12", "libcublas.so.11", + "libcublas.so.12", "libcusparse.so.11", + "libcusparse.so.12", "libcustatevec.so.1", ] print(f"Excluding {libs}") -for p in POLICIES: - p["lib_whitelist"].extend(libs) +for arch in [ + "manylinux2014_x86_64", + "manylinux_2_28_x86_64", + "manylinux2014_aarch64", + "manylinux_2_28_aarch64", + "manylinux2014_ppc64le", + "manylinux_2_28_ppc64le", +]: + wheel_policy = WP() + arch_pol = wheel_policy.get_policy_by_name(arch) + if arch_pol: + arch_pol["lib_whitelist"].extend(libs) if __name__ == "__main__": sys.exit(main()) diff --git a/doc/requirements.txt b/doc/requirements.txt index ff26c3ded6..4887af0718 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -11,7 +11,7 @@ sphinxcontrib-qthelp==1.0.3 sphinxcontrib-htmlhelp==2.0.1 sphinxcontrib-serializinghtml==1.1.5 pennylane-sphinx-theme -custatevec-cu11 +custatevec-cu12 wheel sphinxext-opengraph matplotlib \ No newline at end of file diff --git a/pennylane_lightning/core/lightning_base.py b/pennylane_lightning/core/lightning_base.py index 9b784866f5..e17f83fe3b 100644 --- a/pennylane_lightning/core/lightning_base.py +++ b/pennylane_lightning/core/lightning_base.py @@ -93,13 +93,13 @@ def stopping_condition(self): and observable) and returns ``True`` if supported by the device.""" def accepts_obj(obj): - if obj.name == "QFT" and len(obj.wires) < 10: - return True - if obj.name == "GroverOperator" and len(obj.wires) < 13: - return True - return (not isinstance(obj, qml.tape.QuantumTape)) and getattr( - self, "supports_operation", lambda name: False - )(obj.name) + if obj.name == "QFT": + return len(obj.wires) < 10 + if obj.name == "GroverOperator": + return len(obj.wires) < 13 + is_not_tape = not isinstance(obj, qml.tape.QuantumTape) + is_supported = getattr(self, "supports_operation", lambda name: False)(obj.name) + return is_not_tape and is_supported return qml.BooleanFn(accepts_obj) diff --git a/pennylane_lightning/lightning_gpu/lightning_gpu.py b/pennylane_lightning/lightning_gpu/lightning_gpu.py index 349e821959..fba2612207 100644 --- a/pennylane_lightning/lightning_gpu/lightning_gpu.py +++ b/pennylane_lightning/lightning_gpu/lightning_gpu.py @@ -60,7 +60,7 @@ "cuquantum" ): # pragma: no cover raise ImportError( - 'custatevec libraries not found. Please pip install appropriate custatevec in a virtual environment and then add its path to the "LD_LIBRARY_PATH" environment variable.' + "custatevec libraries not found. Please pip install the appropriate custatevec library in a virtual environment." ) if not DevPool.getTotalDevices(): # pragma: no cover raise ValueError("No supported CUDA-capable device found") diff --git a/requirements-dev.txt b/requirements-dev.txt index 10e01a32cc..94456d4e0f 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -12,5 +12,5 @@ black==23.7.0 clang-tidy~=16.0 clang-format~=16.0 cmake -custatevec-cu11 +custatevec-cu12 pylint diff --git a/tests/test_adjoint_jacobian.py b/tests/test_adjoint_jacobian.py index 86c0a9e246..422ad06eef 100644 --- a/tests/test_adjoint_jacobian.py +++ b/tests/test_adjoint_jacobian.py @@ -727,6 +727,7 @@ def test_controlled_jacobian(self, par, n_qubits, control_value, operation, tol) np.random.seed(1337) init_state = np.random.rand(2**n_qubits) + 1.0j * np.random.rand(2**n_qubits) init_state /= np.sqrt(np.dot(np.conj(init_state), init_state)) + init_state = np.array(init_state, requires_grad=False) if operation.num_wires > n_qubits: return diff --git a/tests/test_decomposition.py b/tests/test_decomposition.py index cfd7cf7fed..a80e955cfd 100644 --- a/tests/test_decomposition.py +++ b/tests/test_decomposition.py @@ -31,12 +31,14 @@ class TestDenseMatrixDecompositionThreshold: input = [ (qml.QFT, 8, True), (qml.QFT, 10, False), + (qml.QFT, 14, False), (qml.GroverOperator, 8, True), (qml.GroverOperator, 13, False), ] @pytest.mark.parametrize("op, n_wires, condition", input) def test_threshold(self, op, n_wires, condition): - wires = np.linspace(0, n_wires - 1, n_wires, dtype=int) + wires = range(n_wires) op = op(wires=wires) - assert ld.stopping_condition.__get__(op)(op) == condition + dev = ld(n_wires) + assert dev.stopping_condition(op) == condition From af41a638245df4800757005d4153d15d613b8e4d Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Tue, 13 Feb 2024 08:59:51 -0500 Subject: [PATCH 004/428] update version --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index 87a919ae46..676b0361c2 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.35.0-dev12" +__version__ = "0.35.0-dev14" \ No newline at end of file From 17a4fb035626027ad1c86bc7286a8a2a26bd88bf Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Tue, 13 Feb 2024 09:02:18 -0500 Subject: [PATCH 005/428] update version --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index 676b0361c2..af34c02f2a 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.35.0-dev14" \ No newline at end of file +__version__ = "0.35.0-dev14" From 7d9c0483c41c43de9dafc7fed500481df9355310 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Tue, 13 Feb 2024 09:04:23 -0500 Subject: [PATCH 006/428] update lightning_qubit_2 --- .../lightning_qubit/lightning_qubit2.py | 299 +++++++++++------- 1 file changed, 178 insertions(+), 121 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index 30cabef7df..6e2fac9ce7 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -21,135 +21,184 @@ import pennylane as qml from pennylane.devices import Device, ExecutionConfig, DefaultExecutionConfig -from pennylane.devices.preprocess import decompose, validate_device_wires, decompose, validate_measurements, validate_observables, no_sampling +from pennylane.devices.preprocess import ( + decompose, + validate_device_wires, + decompose, + validate_measurements, + validate_observables, + no_sampling, +) from pennylane.devices.qubit.sampling import get_num_shots_and_executions from pennylane.tape import QuantumScript from pennylane.transforms.core import TransformProgram from pennylane.typing import Result, ResultBatch +from ._state_vector import LightningStateVector +from ._measurements import LightningMeasurements -def dummy_simulate(circuit, rng=None, c_dtype=np.complex128, batch_obs=False, mcmc=False, - kernel_name="Local", num_burnin=100): - return tuple(0.0 for _ in circuit.measurements) -def dummy_jacobian(circuit): +try: + # pylint: disable=import-error, no-name-in-module + from pennylane_lightning.lightning_qubit_ops import ( + StateVectorC64, + StateVectorC128, + ) + + LQ_CPP_BINARY_AVAILABLE = True +except ImportError: + LQ_CPP_BINARY_AVAILABLE = False + + +def simulate(circuit: QuantumScript, dtype=np.complex128, debugger=None) -> Result: + """Simulate a single quantum script. + + Args: + circuit (QuantumTape): The single circuit to simulate + dtype: Datatypes for state-vector representation. Must be one of + ``np.complex64`` or ``np.complex128``. + debugger (_Debugger): The debugger to use + + Returns: + tuple(TensorLike): The results of the simulation + + Note that this function can return measurements for non-commuting observables simultaneously. + + """ + state, is_state_batched = LightningStateVector( + num_wires=circuit.num_wires, dtype=dtype + ).get_final_state(circuit, debugger=debugger) + return LightningMeasurements(state).measure_final_state(circuit, is_state_batched) + + +def dummy_jacobian(circuit: QuantumScript): return np.array(0.0) -def dummy_simulate_and_jacobian(circuit): + +def simulate_and_jacobian(circuit: QuantumScript): return np.array(0.0), np.array(0.0) + Result_or_ResultBatch = Union[Result, ResultBatch] QuantumTapeBatch = Sequence[qml.tape.QuantumTape] QuantumTape_or_Batch = Union[qml.tape.QuantumTape, QuantumTapeBatch] -_operations = frozenset({ - "Identity", - "BasisState", - "QubitStateVector", - "StatePrep", - "QubitUnitary", - "ControlledQubitUnitary", - "MultiControlledX", - "DiagonalQubitUnitary", - "PauliX", - "PauliY", - "PauliZ", - "MultiRZ", - "Hadamard", - "S", - "Adjoint(S)", - "T", - "Adjoint(T)", - "SX", - "Adjoint(SX)", - "CNOT", - "SWAP", - "ISWAP", - "PSWAP", - "Adjoint(ISWAP)", - "SISWAP", - "Adjoint(SISWAP)", - "SQISW", - "CSWAP", - "Toffoli", - "CY", - "CZ", - "PhaseShift", - "ControlledPhaseShift", - "CPhase", - "RX", - "RY", - "RZ", - "Rot", - "CRX", - "CRY", - "CRZ", - "C(PauliX)", - "C(PauliY)", - "C(PauliZ)", - "C(Hadamard)", - "C(S)", - "C(T)", - "C(PhaseShift)", - "C(RX)", - "C(RY)", - "C(RZ)", - "C(SWAP)", - "C(IsingXX)", - "C(IsingXY)", - "C(IsingYY)", - "C(IsingZZ)", - "C(SingleExcitation)", - "C(SingleExcitationMinus)", - "C(SingleExcitationPlus)", - "C(DoubleExcitation)", - "C(DoubleExcitationMinus)", - "C(DoubleExcitationPlus)", - "CRot", - "IsingXX", - "IsingYY", - "IsingZZ", - "IsingXY", - "SingleExcitation", - "SingleExcitationPlus", - "SingleExcitationMinus", - "DoubleExcitation", - "DoubleExcitationPlus", - "DoubleExcitationMinus", - "QubitCarry", - "QubitSum", - "OrbitalRotation", - "QFT", - "ECR", - "BlockEncode", -}) - -_observables = frozenset({ - "PauliX", - "PauliY", - "PauliZ", - "Hadamard", - "Hermitian", - "Identity", - "Projector", - "SparseHamiltonian", - "Hamiltonian", - "Sum", - "SProd", - "Prod", - "Exp", -}) +_operations = frozenset( + { + "Identity", + "BasisState", + "QubitStateVector", + "StatePrep", + "QubitUnitary", + "ControlledQubitUnitary", + "MultiControlledX", + "DiagonalQubitUnitary", + "PauliX", + "PauliY", + "PauliZ", + "MultiRZ", + "Hadamard", + "S", + "Adjoint(S)", + "T", + "Adjoint(T)", + "SX", + "Adjoint(SX)", + "CNOT", + "SWAP", + "ISWAP", + "PSWAP", + "Adjoint(ISWAP)", + "SISWAP", + "Adjoint(SISWAP)", + "SQISW", + "CSWAP", + "Toffoli", + "CY", + "CZ", + "PhaseShift", + "ControlledPhaseShift", + "CPhase", + "RX", + "RY", + "RZ", + "Rot", + "CRX", + "CRY", + "CRZ", + "C(PauliX)", + "C(PauliY)", + "C(PauliZ)", + "C(Hadamard)", + "C(S)", + "C(T)", + "C(PhaseShift)", + "C(RX)", + "C(RY)", + "C(RZ)", + "C(SWAP)", + "C(IsingXX)", + "C(IsingXY)", + "C(IsingYY)", + "C(IsingZZ)", + "C(SingleExcitation)", + "C(SingleExcitationMinus)", + "C(SingleExcitationPlus)", + "C(DoubleExcitation)", + "C(DoubleExcitationMinus)", + "C(DoubleExcitationPlus)", + "CRot", + "IsingXX", + "IsingYY", + "IsingZZ", + "IsingXY", + "SingleExcitation", + "SingleExcitationPlus", + "SingleExcitationMinus", + "DoubleExcitation", + "DoubleExcitationPlus", + "DoubleExcitationMinus", + "QubitCarry", + "QubitSum", + "OrbitalRotation", + "QFT", + "ECR", + "BlockEncode", + } +) + +_observables = frozenset( + { + "PauliX", + "PauliY", + "PauliZ", + "Hadamard", + "Hermitian", + "Identity", + "Projector", + "SparseHamiltonian", + "Hamiltonian", + "Sum", + "SProd", + "Prod", + "Exp", + } +) + def stopping_condition(op: qml.operation.Operator) -> bool: return op.name in _operations + def accepted_observables(obs: qml.operation.Operator) -> bool: return obs.name in _observables -def accepted_measurements(m : qml.measurements.MeasurementProcess) -> bool: + +def accepted_measurements(m: qml.measurements.MeasurementProcess) -> bool: return isinstance(m, (qml.measurements.ExpectationMP)) + class LightningQubit2(Device): """PennyLane Lightning Qubit device. @@ -182,13 +231,14 @@ class LightningQubit2(Device): qubit is built with OpenMP. """ - name = 'lightning.qubit2' + name = "lightning.qubit2" + _CPP_BINARY_AVAILABLE = LQ_CPP_BINARY_AVAILABLE _device_options = ["rng", "c_dtype", "batch_obs", "mcmc", "kernel_name", "num_burnin"] def __init__( # pylint: disable=too-many-arguments self, - wires=None, + wires=None, # the number of wires are always needed for Lightning. Do we have a better way to get it *, c_dtype=np.complex128, shots=None, @@ -222,19 +272,21 @@ def __init__( # pylint: disable=too-many-arguments self._kernel_name = None self._num_burnin = None + @property + def c_dtype(self): + """State vector complex data type.""" + return self._c_dtype + @property def operation(self) -> frozenset[str]: - """The names of supported operations. - """ + """The names of supported operations.""" return _operations @property def observables(self) -> frozenset[str]: - """The names of supported observables. - """ + """The names of supported observables.""" return _observables - def _setup_execution_config(self, config): updated_values = {} if config.gradient_method == "best": @@ -251,7 +303,6 @@ def _setup_execution_config(self, config): return replace(config, **updated_values, device_options=new_device_options) - def supports_derivatives( self, execution_config: Optional[ExecutionConfig] = None, @@ -263,12 +314,16 @@ def supports_derivatives( return False if circuit is None: return True - return all(isinstance(m, qml.measurements.ExpectationMP) for m in circuit.measurements) and not circuit.shots - + return ( + all(isinstance(m, qml.measurements.ExpectationMP) for m in circuit.measurements) + and not circuit.shots + ) def preprocess(self, execution_config: ExecutionConfig = DefaultExecutionConfig): program = TransformProgram() - program.add_transform(validate_measurements, analytic_measurements=accepted_measurements, name=self.name) + program.add_transform( + validate_measurements, analytic_measurements=accepted_measurements, name=self.name + ) program.add_transform(no_sampling) program.add_transform(validate_observables, accepted_observables, name=self.name) program.add_transform(validate_device_wires, self.wires, name=self.name) @@ -295,7 +350,11 @@ def _execute_tracking(self, circuits): ) self.tracker.record() - def execute(self, circuits : QuantumTape_or_Batch, execution_config: ExecutionConfig = DefaultExecutionConfig) -> Result_or_ResultBatch: + def execute( + self, + circuits: QuantumTape_or_Batch, + execution_config: ExecutionConfig = DefaultExecutionConfig, + ) -> Result_or_ResultBatch: is_single_circuit = False if isinstance(circuits, QuantumScript): is_single_circuit = True @@ -306,11 +365,10 @@ def execute(self, circuits : QuantumTape_or_Batch, execution_config: ExecutionCo results = [] for circuit in circuits: - results.append(dummy_simulate(circuit, **execution_config.device_options)) + results.append(simulate(circuit, **execution_config.device_options)) return results[0] if is_single_circuit else tuple(results) - def compute_derivatives( self, circuits: QuantumTape_or_Batch, @@ -328,7 +386,6 @@ def compute_derivatives( return res[0] if is_single_circuit else res - def execute_and_compute_derivatives( self, circuits: QuantumTape_or_Batch, @@ -349,6 +406,6 @@ def execute_and_compute_derivatives( ) self.tracker.record() - results = tuple(dummy_simulate_and_jacobian(c) for c in circuits) + results = tuple(simulate_and_jacobian(c) for c in circuits) results, jacs = tuple(zip(*results)) - return (results[0], jacs[0]) if is_single_circuit else (results, jacs) \ No newline at end of file + return (results[0], jacs[0]) if is_single_circuit else (results, jacs) From 67b11c5ec56a7eadd491bd5bde4c3177c94cedc1 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Tue, 13 Feb 2024 09:05:47 -0500 Subject: [PATCH 007/428] add LightningQubit2 to init --- pennylane_lightning/lightning_qubit/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pennylane_lightning/lightning_qubit/__init__.py b/pennylane_lightning/lightning_qubit/__init__.py index 53e50cbf00..9d8ddab62d 100644 --- a/pennylane_lightning/lightning_qubit/__init__.py +++ b/pennylane_lightning/lightning_qubit/__init__.py @@ -15,3 +15,4 @@ from pennylane_lightning.core import __version__ from .lightning_qubit import LightningQubit +from .lightning_qubit2 import LightningQubit2 From 6e82a1a17ea074c786aaa5af6c686f0bcf46afc0 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Tue, 13 Feb 2024 09:09:15 -0500 Subject: [PATCH 008/428] add LightningStateVector class --- .../lightning_qubit/_state_vector.py | 386 ++++++++++++++++++ 1 file changed, 386 insertions(+) create mode 100644 pennylane_lightning/lightning_qubit/_state_vector.py diff --git a/pennylane_lightning/lightning_qubit/_state_vector.py b/pennylane_lightning/lightning_qubit/_state_vector.py new file mode 100644 index 0000000000..a4e4c0ca01 --- /dev/null +++ b/pennylane_lightning/lightning_qubit/_state_vector.py @@ -0,0 +1,386 @@ +# Copyright 2018-2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +Class implementation for state-vector manipulation. +""" + +try: + from pennylane_lightning.lightning_qubit_ops import ( + allocate_aligned_array, + get_alignment, + best_alignment, + StateVectorC64, + StateVectorC128, + ) +except ImportError: + pass + +import numpy as np + +import pennylane as qml +from pennylane.tape import QuantumScript +from pennylane.wires import Wires +from pennylane import ( + BasisState, + StatePrep, + DeviceError, +) + +from itertools import product + + +class LightningStateVector: + """Lightning state-vector class. + + Interfaces with C++ python binding methods for state-vector manipulation. + + Args: + num_wires(int): the number of wires to initialize the device with + dtype: Datatypes for state-vector representation. Must be one of + ``np.complex64`` or ``np.complex128``. + device_name(string): state vector device name. Options: ["lightning.qubit"] + """ + + def __init__(self, num_wires, dtype=np.complex128, device_name="lightning.qubit"): + self.num_wires = num_wires + self._wires = Wires(range(num_wires)) + self._dtype = dtype + self._name = device_name + self._qubit_state = self._state_dtype()(self.num_wires) + + @property + def dtype(self): + """Returns the state vector data type.""" + return self._dtype + + @property + def name(self): + """Returns the state vector device name.""" + return self._name + + @property + def wires(self): + """All wires that can be addressed on this device""" + return self._wires + + def _state_dtype(self): + """Binding to Lightning Managed state vector. + + Args: + dtype (complex): Data complex type + + Returns: the state vector class + """ + if self.dtype not in [np.complex128, np.complex64]: # pragma: no cover + raise ValueError( + f"Data type is not supported for state-vector computation: {self.dtype}" + ) + return StateVectorC128 if self.dtype == np.complex128 else StateVectorC64 + + @staticmethod + def _asarray(arr, dtype=None): + """Verify data alignment and allocate aligned memory if needed. + + Args: + arr (numpy.array): data array + dtype (dtype, optional): if provided will convert the array data type. + + Returns: + np.array: numpy array with aligned data. + """ + arr = np.asarray(arr) # arr is not copied + + if arr.dtype.kind not in ["f", "c"]: + return arr + + if not dtype: + dtype = arr.dtype + + # We allocate a new aligned memory and copy data to there if alignment or dtype + # mismatches + # Note that get_alignment does not necessarily return CPUMemoryModel(Unaligned) + # numpy allocated memory as the memory location happens to be aligned. + if int(get_alignment(arr)) < int(best_alignment()) or arr.dtype != dtype: + new_arr = allocate_aligned_array(arr.size, np.dtype(dtype), False).reshape(arr.shape) + if len(arr.shape): + new_arr[:] = arr + else: + np.copyto(new_arr, arr) + arr = new_arr + return arr + + def _create_basis_state(self, index): + """Return a computational basis state over all wires. + Args: + _qubit_state: a handle to Lightning qubit state. + index (int): integer representing the computational basis state. + """ + self._qubit_state.setBasisState(index) + + def reset_state(self): + """Reset the device's state""" + # init the state vector to |00..0> + self._qubit_state.resetStateVector() + + @property + def state(self): + """Copy the state vector data to a numpy array. + + **Example** + + >>> dev = qml.device('lightning.qubit', wires=1) + >>> dev.apply([qml.PauliX(wires=[0])]) + >>> print(dev.state) + [0.+0.j 1.+0.j] + """ + state = np.zeros(2**self.num_wires, dtype=self.dtype) + state = self._asarray(state, dtype=self.dtype) + self._qubit_state.getState(state) + return state + + @property + def state_vector(self): + """Returns a handle to the state vector.""" + return self._qubit_state + + def _preprocess_state_vector(self, state, device_wires): + """Initialize the internal state vector in a specified state. + + Args: + state (array[complex]): normalized input state of length ``2**len(wires)`` + or broadcasted state of shape ``(batch_size, 2**len(wires))`` + device_wires (Wires): wires that get initialized in the state + + Returns: + array[complex]: normalized input state of length ``2**len(wires)`` + or broadcasted state of shape ``(batch_size, 2**len(wires))`` + array[int]: indices for which the state is changed to input state vector elements + """ + + # translate to wire labels used by device + device_wires = self.map_wires(device_wires) + + # special case for integral types + if state.dtype.kind == "i": + state = qml.numpy.array(state, dtype=self.C_DTYPE) + state = self._asarray(state, dtype=self.C_DTYPE) + + if len(device_wires) == self.num_wires and Wires(sorted(device_wires)) == device_wires: + return None, state + + # generate basis states on subset of qubits via the cartesian product + basis_states = np.array(list(product([0, 1], repeat=len(device_wires)))) + + # get basis states to alter on full set of qubits + unravelled_indices = np.zeros((2 ** len(device_wires), self.num_wires), dtype=int) + unravelled_indices[:, device_wires] = basis_states + + # get indices for which the state is changed to input state vector elements + ravelled_indices = np.ravel_multi_index(unravelled_indices.T, [2] * self.num_wires) + return ravelled_indices, state + + def _get_basis_state_index(self, state, wires): + """Returns the basis state index of a specified computational basis state. + + Args: + state (array[int]): computational basis state of shape ``(wires,)`` + consisting of 0s and 1s + wires (Wires): wires that the provided computational state should be initialized on + + Returns: + int: basis state index + """ + # translate to wire labels used by device + device_wires = self.map_wires(wires) + + # length of basis state parameter + n_basis_state = len(state) + + if not set(state.tolist()).issubset({0, 1}): + raise ValueError("BasisState parameter must consist of 0 or 1 integers.") + + if n_basis_state != len(device_wires): + raise ValueError("BasisState parameter and wires must be of equal length.") + + # get computational basis state number + basis_states = 2 ** (self.num_wires - 1 - np.array(device_wires)) + basis_states = qml.math.convert_like(basis_states, state) + return int(qml.math.dot(state, basis_states)) + + def _apply_state_vector(self, state, device_wires): + """Initialize the internal state vector in a specified state. + Args: + state (array[complex]): normalized input state of length ``2**len(wires)`` + or broadcasted state of shape ``(batch_size, 2**len(wires))`` + device_wires (Wires): wires that get initialized in the state + """ + + if isinstance(state, self._qubit_state.__class__): + state_data = allocate_aligned_array(state.size, np.dtype(self.dtype), True) + self._qubit_state.getState(state_data) + state = state_data + + ravelled_indices, state = self._preprocess_state_vector(state, device_wires) + + # translate to wire labels used by device + device_wires = self.map_wires(device_wires) + output_shape = [2] * self.num_wires + + if len(device_wires) == self.num_wires and Wires(sorted(device_wires)) == device_wires: + # Initialize the entire device state with the input state + state = self._reshape(state, output_shape).ravel(order="C") + self._qubit_state.UpdateData(state) + return + + self._qubit_state.setStateVector(ravelled_indices, state) + + def _apply_basis_state(self, state, wires): + """Initialize the state vector in a specified computational basis state. + + Args: + state (array[int]): computational basis state of shape ``(wires,)`` + consisting of 0s and 1s. + wires (Wires): wires that the provided computational state should be + initialized on + + Note: This function does not support broadcasted inputs yet. + """ + num = self._get_basis_state_index(state, wires) + self._create_basis_state(num) + + def _apply_lightning_controlled(self, operation): + """Apply an arbitrary controlled operation to the state tensor. + + Args: + operation (~pennylane.operation.Operation): operation to apply + + Returns: + array[complex]: the output state tensor + """ + state = self.state_vector + + basename = "PauliX" if operation.name == "MultiControlledX" else operation.base.name + if basename == "Identity": + return + method = getattr(state, f"{basename}", None) + control_wires = self.wires.indices(operation.control_wires) + control_values = ( + [bool(int(i)) for i in operation.hyperparameters["control_values"]] + if operation.name == "MultiControlledX" + else operation.control_values + ) + if operation.name == "MultiControlledX": + target_wires = list(set(self.wires.indices(operation.wires)) - set(control_wires)) + else: + target_wires = self.wires.indices(operation.target_wires) + if method is not None: # apply n-controlled specialized gate + inv = False + param = operation.parameters + method(control_wires, control_values, target_wires, inv, param) + else: # apply gate as an n-controlled matrix + method = getattr(state, "applyControlledMatrix") + target_wires = self.wires.indices(operation.target_wires) + try: + method( + qml.matrix(operation.base), + control_wires, + control_values, + target_wires, + False, + ) + except AttributeError: # pragma: no cover + # To support older versions of PL + method(operation.base.matrix, control_wires, control_values, target_wires, False) + + def apply_lightning(self, operations): + """Apply a list of operations to the state tensor. + + Args: + operations (list[~pennylane.operation.Operation]): operations to apply + + Returns: + array[complex]: the output state tensor + """ + state = self.state_vector + + # Skip over identity operations instead of performing + # matrix multiplication with it. + for operation in operations: + name = operation.name + if name == "Identity": + continue + method = getattr(state, name, None) + wires = self.wires.indices(operation.wires) + + if method is not None: # apply specialized gate + inv = False + param = operation.parameters + method(wires, inv, param) + elif ( + name[0:2] == "C(" or name == "ControlledQubitUnitary" or name == "MultiControlledX" + ): # apply n-controlled gate + print("hi") + self._apply_lightning_controlled(operation) + else: # apply gate as a matrix + # Inverse can be set to False since qml.matrix(operation) is already in + # inverted form + method = getattr(state, "applyMatrix") + try: + method(qml.matrix(operation), wires, False) + except AttributeError: # pragma: no cover + # To support older versions of PL + method(operation.matrix, wires, False) + + def apply_operations(self, operations): + """Applies operations to the state vector.""" + # State preparation is currently done in Python + if operations: # make sure operations[0] exists + if isinstance(operations[0], StatePrep): + self._apply_state_vector(operations[0].parameters[0].copy(), operations[0].wires) + operations = operations[1:] + elif isinstance(operations[0], BasisState): + self._apply_basis_state(operations[0].parameters[0], operations[0].wires) + operations = operations[1:] + + for operation in operations: + if isinstance(operation, (StatePrep, BasisState)): + raise DeviceError( + f"Operation {operation.name} cannot be used after other " + f"Operations have already been applied on a {self.short_name} device." + ) + + self.apply_lightning(operations) + + def get_final_state(self, circuit: QuantumScript, debugger=None): + """ + Get the final state that results from executing the given quantum script. + + This is an internal function that will be called by the successor to ``lightning.qubit``. + + Args: + circuit (QuantumScript): The single circuit to simulate + debugger (._Debugger): The debugger to use + + Returns: + Tuple: A tuple containing the Lightning final state handler of the quantum script and + whether the state has a batch dimension. + + """ + + circuit = circuit.map_to_standard_wires() + self.apply_operations(circuit._ops) + + # No batching support yet. + + return self, False # is_state_batched From 0df46018c654e391248d497716618760813dbd33 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Tue, 13 Feb 2024 09:10:42 -0500 Subject: [PATCH 009/428] add LightningMeasurements class --- .../lightning_qubit/_measurements.py | 182 ++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 pennylane_lightning/lightning_qubit/_measurements.py diff --git a/pennylane_lightning/lightning_qubit/_measurements.py b/pennylane_lightning/lightning_qubit/_measurements.py new file mode 100644 index 0000000000..a1de9000c9 --- /dev/null +++ b/pennylane_lightning/lightning_qubit/_measurements.py @@ -0,0 +1,182 @@ +# Copyright 2018-2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +Class implementation for state vector measurements. +""" + +try: + from pennylane_lightning.lightning_qubit_ops import ( + MeasurementsC64, + MeasurementsC128, + ) +except ImportError: + pass + +import numpy as np +from typing import Callable, List + +import pennylane as qml +from pennylane.measurements import StateMeasurement, MeasurementProcess, ExpectationMP +from pennylane.typing import TensorLike, Result +from pennylane.tape import QuantumScript +from pennylane.wires import Wires + +from typing import List + +from ._serialize import QuantumScriptSerializer +from ._state_vector import LightningStateVector + + +class LightningMeasurements: + """Lightning Measurements class + + Measures the state provided by the LightningStateVector class. + + Args: + qubit_state(LightningStateVector) + """ + + def __init__(self, qubit_state: LightningStateVector) -> None: + self._qubit_state = qubit_state + self.state = qubit_state.state_vector + self.dtype = qubit_state.dtype + self.measurement_lightning = self._measurement_dtype()(self.state) + + def _measurement_dtype(self): + """Binding to Lightning Managed state vector. + + Args: + dtype (complex): Data complex type + + Returns: the state vector class + """ + return MeasurementsC64 if self.dtype == np.complex64 else MeasurementsC128 + + def state_diagonalizing_gates(self, measurementprocess: StateMeasurement) -> TensorLike: + """Apply a measurement to state when the measurement process has an observable with diagonalizing gates. + This method will is bypassing the measurement process to default.qubit implementation. + + Args: + measurementprocess (StateMeasurement): measurement to apply to the state + + Returns: + TensorLike: the result of the measurement + """ + self._qubit_state.apply_operations(measurementprocess.diagonalizing_gates()) + + state_array = self._qubit_state.state + total_wires = int(np.log2(state_array.size)) + wires = Wires(range(total_wires)) + return measurementprocess.process_state(state_array, wires) + + def expval(self, measurementprocess: MeasurementProcess): + """Expectation value of the supplied observable contained in the MeasurementProcess. + + Args: + measurementprocess (StateMeasurement): measurement to apply to the state + + Returns: + Expectation value of the observable + """ + + if measurementprocess.obs.name == "SparseHamiltonian": + # ensuring CSR sparse representation. + CSR_SparseHamiltonian = measurementprocess.obs.sparse_matrix( + wire_order=list(range(self._qubit_state.num_wires)) + ).tocsr(copy=False) + return self.measurement_lightning.expval( + CSR_SparseHamiltonian.indptr, + CSR_SparseHamiltonian.indices, + CSR_SparseHamiltonian.data, + ) + + if ( + measurementprocess.obs.name in ["Hamiltonian", "Hermitian"] + or (measurementprocess.obs.arithmetic_depth > 0) + or isinstance(measurementprocess.obs.name, List) + ): + ob_serialized = QuantumScriptSerializer( + self._qubit_state.name, self.dtype == np.complex64 + )._ob(measurementprocess.obs) + return self.measurement_lightning.expval(ob_serialized) + + return self.measurement_lightning.expval( + measurementprocess.obs.name, measurementprocess.obs.wires + ) + + def get_measurement_function( + self, measurementprocess: MeasurementProcess + ) -> Callable[[MeasurementProcess, TensorLike], TensorLike]: + """Get the appropriate method for performing a measurement. + + Args: + measurementprocess (MeasurementProcess): measurement process to apply to the state + + Returns: + Callable: function that returns the measurement result + """ + if isinstance(measurementprocess, StateMeasurement): + if isinstance(measurementprocess, ExpectationMP): + if measurementprocess.obs.name in [ + "Identity", + "Projector", + ]: + return self.state_diagonalizing_gates + return self.expval + + if measurementprocess.obs is None or measurementprocess.obs.has_diagonalizing_gates: + return self.state_diagonalizing_gates + + raise NotImplementedError + + def measurement(self, measurementprocess: MeasurementProcess) -> TensorLike: + """Apply a measurement process to a state. + + Args: + measurementprocess (MeasurementProcess): measurement process to apply to the state + + Returns: + TensorLike: the result of the measurement + """ + return self.get_measurement_function(measurementprocess)(measurementprocess) + + def measure_final_state(self, circuit: QuantumScript, is_state_batched) -> Result: + """ + Perform the measurements required by the circuit on the provided state. + + This is an internal function that will be called by the successor to ``lightning.qubit``. + + Args: + circuit (QuantumScript): The single circuit to simulate + is_state_batched (bool): Whether the state has a batch dimension or not. + + Returns: + Tuple[TensorLike]: The measurement results + """ + if set(circuit.wires) != set(range(circuit.num_wires)): + wire_map = {w: i for i, w in enumerate(circuit.wires)} + circuit = qml.map_wires(circuit, wire_map) + + if not circuit.shots: + # analytic case + if len(circuit.measurements) == 1: + return self.measurement(circuit.measurements[0]) + + results = [] + for mp in circuit.measurements: + measure_val = self.measurement(mp) + results += [ + measure_val, + ] + return tuple(results) From 79e4d646752c11dd7d0ea330e291395c6812016f Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Tue, 13 Feb 2024 09:11:44 -0500 Subject: [PATCH 010/428] add new QuantumScriptSerializer class --- .../lightning_qubit/_serialize.py | 390 ++++++++++++++++++ 1 file changed, 390 insertions(+) create mode 100644 pennylane_lightning/lightning_qubit/_serialize.py diff --git a/pennylane_lightning/lightning_qubit/_serialize.py b/pennylane_lightning/lightning_qubit/_serialize.py new file mode 100644 index 0000000000..2553126c6f --- /dev/null +++ b/pennylane_lightning/lightning_qubit/_serialize.py @@ -0,0 +1,390 @@ +# Copyright 2018-2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +r""" +Helper functions for serializing quantum tapes. +""" +from typing import List, Tuple +import numpy as np +from pennylane import ( + BasisState, + Hadamard, + PauliX, + PauliY, + PauliZ, + Identity, + StatePrep, + Rot, + Hamiltonian, + SparseHamiltonian, + QubitUnitary, +) +from pennylane.operation import Tensor +from pennylane.tape import QuantumTape +from pennylane.math import unwrap + +from pennylane import matrix, DeviceError + +pauli_name_map = { + "I": "Identity", + "X": "PauliX", + "Y": "PauliY", + "Z": "PauliZ", +} + + +class QuantumScriptSerializer: + """Serializer class for `pennylane.tape.QuantumScript` data. + + Args: + device_name: device shortname. + use_csingle (bool): whether to use np.complex64 instead of np.complex128 + + """ + + # pylint: disable=import-outside-toplevel, too-many-instance-attributes + def __init__( + self, device_name, use_csingle: bool = False, use_mpi: bool = False, split_obs: bool = False + ): + self.use_csingle = use_csingle + self.device_name = device_name + self.split_obs = split_obs + if device_name == "lightning.qubit" or device_name == "lightning.qubit2": + try: + import pennylane_lightning.lightning_qubit_ops as lightning_ops + except ImportError as exception: + raise ImportError( + f"Pre-compiled binaries for {device_name}" + " serialize functionality are not available." + ) from exception + elif device_name == "lightning.kokkos": + try: + import pennylane_lightning.lightning_kokkos_ops as lightning_ops + except ImportError as exception: + raise ImportError( + f"Pre-compiled binaries for {device_name}" + " serialize functionality are not available." + ) from exception + elif device_name == "lightning.gpu": + try: + import pennylane_lightning.lightning_gpu_ops as lightning_ops + except ImportError as exception: + raise ImportError( + f"Pre-compiled binaries for {device_name} are not available." + ) from exception + else: + raise DeviceError(f'The device name "{device_name}" is not a valid option.') + self.statevector_c64 = lightning_ops.StateVectorC64 + self.statevector_c128 = lightning_ops.StateVectorC128 + self.named_obs_c64 = lightning_ops.observables.NamedObsC64 + self.named_obs_c128 = lightning_ops.observables.NamedObsC128 + self.hermitian_obs_c64 = lightning_ops.observables.HermitianObsC64 + self.hermitian_obs_c128 = lightning_ops.observables.HermitianObsC128 + self.tensor_prod_obs_c64 = lightning_ops.observables.TensorProdObsC64 + self.tensor_prod_obs_c128 = lightning_ops.observables.TensorProdObsC128 + self.hamiltonian_c64 = lightning_ops.observables.HamiltonianC64 + self.hamiltonian_c128 = lightning_ops.observables.HamiltonianC128 + self.sparse_hamiltonian_c64 = lightning_ops.observables.SparseHamiltonianC64 + self.sparse_hamiltonian_c128 = lightning_ops.observables.SparseHamiltonianC128 + + self._use_mpi = use_mpi + + if self._use_mpi: + self.statevector_mpi_c64 = lightning_ops.StateVectorMPIC64 + self.statevector_mpi_c128 = lightning_ops.StateVectorMPIC128 + self.named_obs_mpi_c64 = lightning_ops.observablesMPI.NamedObsMPIC64 + self.named_obs_mpi_c128 = lightning_ops.observablesMPI.NamedObsMPIC128 + self.hermitian_obs_mpi_c64 = lightning_ops.observablesMPI.HermitianObsMPIC64 + self.hermitian_obs_mpi_c128 = lightning_ops.observablesMPI.HermitianObsMPIC128 + self.tensor_prod_obs_mpi_c64 = lightning_ops.observablesMPI.TensorProdObsMPIC64 + self.tensor_prod_obs_mpi_c128 = lightning_ops.observablesMPI.TensorProdObsMPIC128 + self.hamiltonian_mpi_c64 = lightning_ops.observablesMPI.HamiltonianMPIC64 + self.hamiltonian_mpi_c128 = lightning_ops.observablesMPI.HamiltonianMPIC128 + self.sparse_hamiltonian_mpi_c64 = lightning_ops.observablesMPI.SparseHamiltonianMPIC64 + self.sparse_hamiltonian_mpi_c128 = lightning_ops.observablesMPI.SparseHamiltonianMPIC128 + + self._mpi_manager = lightning_ops.MPIManager + + @property + def ctype(self): + """Complex type.""" + return np.complex64 if self.use_csingle else np.complex128 + + @property + def rtype(self): + """Real type.""" + return np.float32 if self.use_csingle else np.float64 + + @property + def sv_type(self): + """State vector matching ``use_csingle`` precision (and MPI if it is supported).""" + if self._use_mpi: + return self.statevector_mpi_c64 if self.use_csingle else self.statevector_mpi_c128 + return self.statevector_c64 if self.use_csingle else self.statevector_c128 + + @property + def named_obs(self): + """Named observable matching ``use_csingle`` precision.""" + if self._use_mpi: + return self.named_obs_mpi_c64 if self.use_csingle else self.named_obs_mpi_c128 + return self.named_obs_c64 if self.use_csingle else self.named_obs_c128 + + @property + def hermitian_obs(self): + """Hermitian observable matching ``use_csingle`` precision.""" + if self._use_mpi: + return self.hermitian_obs_mpi_c64 if self.use_csingle else self.hermitian_obs_mpi_c128 + return self.hermitian_obs_c64 if self.use_csingle else self.hermitian_obs_c128 + + @property + def tensor_obs(self): + """Tensor product observable matching ``use_csingle`` precision.""" + if self._use_mpi: + return ( + self.tensor_prod_obs_mpi_c64 if self.use_csingle else self.tensor_prod_obs_mpi_c128 + ) + return self.tensor_prod_obs_c64 if self.use_csingle else self.tensor_prod_obs_c128 + + @property + def hamiltonian_obs(self): + """Hamiltonian observable matching ``use_csingle`` precision.""" + if self._use_mpi: + return self.hamiltonian_mpi_c64 if self.use_csingle else self.hamiltonian_mpi_c128 + return self.hamiltonian_c64 if self.use_csingle else self.hamiltonian_c128 + + @property + def sparse_hamiltonian_obs(self): + """SparseHamiltonian observable matching ``use_csingle`` precision.""" + if self._use_mpi: + return ( + self.sparse_hamiltonian_mpi_c64 + if self.use_csingle + else self.sparse_hamiltonian_mpi_c128 + ) + return self.sparse_hamiltonian_c64 if self.use_csingle else self.sparse_hamiltonian_c128 + + def _named_obs(self, observable): + """Serializes a Named observable""" + wires = observable.wires.tolist() + if observable.name == "Identity": + wires = wires[:1] + return self.named_obs(observable.name, wires) + + def _hermitian_ob(self, observable): + """Serializes a Hermitian observable""" + assert not isinstance(observable, Tensor) + + return self.hermitian_obs( + matrix(observable).ravel().astype(self.ctype), observable.wires.tolist() + ) + + def _tensor_ob(self, observable): + """Serialize a tensor observable""" + assert isinstance(observable, Tensor) + return self.tensor_obs([self._ob(obs) for obs in observable.obs]) + + def _hamiltonian(self, observable): + coeffs = np.array(unwrap(observable.coeffs)).astype(self.rtype) + terms = [self._ob(t) for t in observable.ops] + + if self.split_obs: + return [self.hamiltonian_obs([c], [t]) for (c, t) in zip(coeffs, terms)] + + return self.hamiltonian_obs(coeffs, terms) + + def _sparse_hamiltonian(self, observable): + """Serialize an observable (Sparse Hamiltonian) + + Args: + observable (Observable): the input observable (Sparse Hamiltonian) + wire_map (dict): a dictionary mapping input wires to the device's backend wires + + Returns: + sparse_hamiltonian_obs (SparseHamiltonianC64 or SparseHamiltonianC128): A Sparse Hamiltonian observable object compatible with the C++ backend + """ + + if self._use_mpi: + Hmat = Hamiltonian([1.0], [Identity(0)]).sparse_matrix() + H_sparse = SparseHamiltonian(Hmat, wires=range(1)) + spm = H_sparse.sparse_matrix() + # Only root 0 needs the overall sparse matrix data + if self._mpi_manager().getRank() == 0: + spm = observable.sparse_matrix() + self._mpi_manager().Barrier() + else: + spm = observable.sparse_matrix() + data = np.array(spm.data).astype(self.ctype) + indices = np.array(spm.indices).astype(np.int64) + offsets = np.array(spm.indptr).astype(np.int64) + + return self.sparse_hamiltonian_obs(data, indices, offsets, observable.wires.tolist()) + + def _pauli_word(self, observable): + """Serialize a :class:`pennylane.pauli.PauliWord` into a Named or Tensor observable.""" + if len(observable) == 1: + wire, pauli = list(observable.items())[0] + return self.named_obs(pauli_name_map[pauli], [wire]) + + return self.tensor_obs( + [self.named_obs(pauli_name_map[pauli], [wire]) for wire, pauli in observable.items()] + ) + + def _pauli_sentence(self, observable): + """Serialize a :class:`pennylane.pauli.PauliSentence` into a Hamiltonian.""" + pwords, coeffs = zip(*observable.items()) + terms = [self._pauli_word(pw) for pw in pwords] + coeffs = np.array(coeffs).astype(self.rtype) + + if self.split_obs: + return [self.hamiltonian_obs([c], [t]) for (c, t) in zip(coeffs, terms)] + return self.hamiltonian_obs(coeffs, terms) + + # pylint: disable=protected-access + def _ob(self, observable): + """Serialize a :class:`pennylane.operation.Observable` into an Observable.""" + if isinstance(observable, Tensor): + return self._tensor_ob(observable) + if observable.name == "Hamiltonian": + return self._hamiltonian(observable) + if observable.name == "SparseHamiltonian": + return self._sparse_hamiltonian(observable) + if isinstance(observable, (PauliX, PauliY, PauliZ, Identity, Hadamard)): + return self._named_obs(observable) + if observable._pauli_rep is not None: + return self._pauli_sentence(observable._pauli_rep) + return self._hermitian_ob(observable) + + def serialize_observables(self, tape: QuantumTape) -> List: + """Serializes the observables of an input tape. + + Args: + tape (QuantumTape): the input quantum tape + + Returns: + list(ObsStructC128 or ObsStructC64): A list of observable objects compatible with + the C++ backend + """ + + serialized_obs = [] + offset_indices = [0] + + for observable in tape.observables: + ser_ob = self._ob(observable) + if isinstance(ser_ob, list): + serialized_obs.extend(ser_ob) + offset_indices.append(offset_indices[-1] + len(ser_ob)) + else: + serialized_obs.append(ser_ob) + offset_indices.append(offset_indices[-1] + 1) + return serialized_obs, offset_indices + + def serialize_ops( + self, tape: QuantumTape + ) -> Tuple[ + List[List[str]], + List[np.ndarray], + List[List[int]], + List[bool], + List[np.ndarray], + List[List[int]], + List[List[bool]], + ]: + """Serializes the operations of an input tape. + + The state preparation operations are not included. + + Args: + tape (QuantumTape): the input quantum tape + + Returns: + Tuple[list, list, list, list, list]: A serialization of the operations, containing a + list of operation names, a list of operation parameters, a list of observable wires, + a list of inverses, a list of matrices for the operations that do not have a + dedicated kernel, a list of controlled wires and a list of controlled values. + """ + names = [] + params = [] + controlled_wires = [] + controlled_values = [] + wires = [] + mats = [] + + uses_stateprep = False + + def get_wires(operation, single_op): + if operation.name[0:2] == "C(" or operation.name == "MultiControlledX": + name = "PauliX" if operation.name == "MultiControlledX" else operation.base.name + controlled_wires_list = operation.control_wires + if operation.name == "MultiControlledX": + wires_list = list(set(operation.wires) - set(controlled_wires_list)) + else: + wires_list = operation.target_wires + control_values_list = ( + [bool(int(i)) for i in operation.hyperparameters["control_values"]] + if operation.name == "MultiControlledX" + else operation.control_values + ) + if not hasattr(self.sv_type, name): + single_op = QubitUnitary(matrix(single_op.base), single_op.base.wires) + name = single_op.name + else: + name = single_op.name + wires_list = single_op.wires.tolist() + controlled_wires_list = [] + control_values_list = [] + return single_op, name, list(wires_list), controlled_wires_list, control_values_list + + for operation in tape.operations: + if isinstance(operation, (BasisState, StatePrep)): + uses_stateprep = True + continue + if isinstance(operation, Rot): + op_list = operation.expand().operations + else: + op_list = [operation] + + for single_op in op_list: + ( + single_op, + name, + wires_list, + controlled_wires_list, + controlled_values_list, + ) = get_wires(operation, single_op) + names.append(name) + # QubitUnitary is a special case, it has a parameter which is not differentiable. + # We thus pass a dummy 0.0 parameter which will not be referenced + if name == "QubitUnitary": + params.append([0.0]) + mats.append(matrix(single_op)) + elif not hasattr(self.sv_type, name): + params.append([]) + mats.append(matrix(single_op)) + else: + params.append(single_op.parameters) + mats.append([]) + + controlled_values.append(controlled_values_list) + controlled_wires.append(list(controlled_wires_list)) + wires.append(wires_list) + + inverses = [False] * len(names) + return ( + names, + params, + wires, + inverses, + mats, + controlled_wires, + controlled_values, + ), uses_stateprep From 194696af568320588ac4a3eed8debc5932a2da1c Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Tue, 13 Feb 2024 09:14:28 -0500 Subject: [PATCH 011/428] allow lightning.qubit2 to be tested within our suite --- tests/conftest.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index e10a3c203c..25b7ada6e6 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -87,7 +87,7 @@ def n_subsystems(request): # Looking for the device for testing. default_device = "lightning.qubit" -supported_devices = {"lightning.kokkos", "lightning.qubit", "lightning.gpu"} +supported_devices = {"lightning.kokkos", "lightning.qubit", "lightning.qubit2", "lightning.gpu"} supported_devices.update({sb.replace(".", "_") for sb in supported_devices}) @@ -113,10 +113,10 @@ def get_device(): device_name = get_device() -if device_name not in qml.plugin_devices: - raise qml.DeviceError( - f"Device {device_name} does not exist. Make sure the required plugin is installed." - ) +# if device_name not in qml.plugin_devices: +# raise qml.DeviceError( +# f"Device {device_name} does not exist. Make sure the required plugin is installed." +# ) # Device specification import pennylane_lightning.lightning_qubit as lightning_ops # Any definition of lightning_ops will do @@ -131,6 +131,11 @@ def get_device(): if hasattr(pennylane_lightning, "lightning_gpu_ops"): import pennylane_lightning.lightning_gpu_ops as lightning_ops +elif device_name == "lightning.qubit2": + from pennylane_lightning.lightning_qubit import LightningQubit2 as LightningDevice + + if hasattr(pennylane_lightning, "lightning_qubit_ops"): + import pennylane_lightning.lightning_qubit_ops as lightning_ops else: from pennylane_lightning.lightning_qubit import LightningQubit as LightningDevice From ecf1bc6e59490b729b7eb997f94c0ad73b3808a2 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Tue, 13 Feb 2024 09:15:01 -0500 Subject: [PATCH 012/428] add tests and CI workflow for lightning_qubit_2 --- .github/workflows/tests_linux.yml | 52 +- tests/lightning_qubit2/test_expval_2.py | 404 ++++++++++++++ tests/lightning_qubit2/test_serialize_2.py | 596 +++++++++++++++++++++ 3 files changed, 1050 insertions(+), 2 deletions(-) create mode 100644 tests/lightning_qubit2/test_expval_2.py create mode 100644 tests/lightning_qubit2/test_serialize_2.py diff --git a/.github/workflows/tests_linux.yml b/.github/workflows/tests_linux.yml index cf34e33a82..71aa8284b9 100644 --- a/.github/workflows/tests_linux.yml +++ b/.github/workflows/tests_linux.yml @@ -197,6 +197,54 @@ jobs: ./main/.coverage-${{ github.job }}-${{ matrix.pl_backend }} if-no-files-found: error + pythontests_LQ_2: + strategy: + matrix: + os: [ubuntu-22.04] + pl_backend: ["lightning_qubit2"] + timeout-minutes: 30 + name: Python tests + runs-on: ${{ matrix.os }} + + steps: + - name: Checkout PennyLane-Lightning + uses: actions/checkout@v3 + with: + fetch-tags: true + path: main + + - uses: actions/setup-python@v4 + name: Install Python + with: + python-version: '3.9' + + - name: Install dependencies + run: sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION + + - name: Get required Python packages + run: | + cd main + python -m pip install -r requirements-dev.txt + python -m pip install openfermionpyscf + + - name: Checkout PennyLane for release build + uses: actions/checkout@v3 + with: + path: pennylane + repository: PennyLaneAI/pennylane + + - name: Install backend device + run: | + cd main + CMAKE_ARGS="-DPL_BACKEND=${{ matrix.pl_backend }} -DLQ_ENABLE_KERNEL_OMP=ON -DENABLE_PYTHON=ON -DCMAKE_CXX_COMPILER=$(which g++-$GCC_VERSION)" \ + python -m pip install -e . -vv + + - name: Run PennyLane-Lightning unit tests + run: | + cd main/ + DEVICENAME=`echo ${{ matrix.pl_backend }} | sed "s/_/./g"` + PL_DEVICE=${DEVICENAME} python -m pytest pytest tests/lightning_qubit2/ + cpptestswithOpenBLAS: if: ${{ !contains(fromJSON('["schedule", "workflow_dispatch"]'), github.event_name) }} strategy: @@ -237,7 +285,7 @@ jobs: lcov --directory . -b ../pennylane_lightning/core/src --capture --output-file coverage.info lcov --remove coverage.info '/usr/*' --output-file coverage.info mv coverage.info coverage-${{ github.job }}-${{ matrix.pl_backend }}-Lapack.info - + - name: Upload test results uses: actions/upload-artifact@v3 if: always() @@ -642,7 +690,7 @@ jobs: - name: Combine coverage files run: | - python -m pip install coverage + python -m pip install coverage python -m coverage combine .coverage-python* python -m coverage xml -o coverage-${{ github.job }}.xml diff --git a/tests/lightning_qubit2/test_expval_2.py b/tests/lightning_qubit2/test_expval_2.py new file mode 100644 index 0000000000..cb66797b3d --- /dev/null +++ b/tests/lightning_qubit2/test_expval_2.py @@ -0,0 +1,404 @@ +# Copyright 2018-2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +Tests for process and execute (expval calculation). +""" +import pytest + +import numpy as np +import pennylane as qml +from pennylane_lightning.lightning_qubit import LightningQubit2 as LightningQubit +from pennylane.devices import DefaultQubit + +from conftest import LightningDevice # tested device + +if not LightningQubit._CPP_BINARY_AVAILABLE: + pytest.skip("No binary module found. Skipping.", allow_module_level=True) + +if LightningDevice != LightningQubit: + pytest.skip("Exclusive tests for lightning.qubit. Skipping.", allow_module_level=True) + +THETA = np.linspace(0.11, 1, 3) +PHI = np.linspace(0.32, 1, 3) +VARPHI = np.linspace(0.02, 1, 3) + + +@pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI))) +class TestExpval: + """Test expectation value calculations""" + + @pytest.fixture(params=[np.complex64, np.complex128]) + def dev(self, request): + return LightningQubit(c_dtype=request.param) + + @staticmethod + def calculate_reference(tape): + dev = DefaultQubit(max_workers=1) + program, _ = dev.preprocess() + tapes, transf_fn = program([tape]) + results = dev.execute(tapes) + return transf_fn(results) + + @staticmethod + def process_and_execute(dev, tape): + program, _ = dev.preprocess() + tapes, transf_fn = program([tape]) + results = dev.execute(tapes) + return transf_fn(results) + + def test_Identity(self, theta, phi, dev, tol): + """Tests applying identities.""" + + with qml.tape.QuantumTape() as tape: + qml.Identity(wires=[0]) + qml.Identity(wires=[0, 1]) + qml.Identity(wires=[1, 2]) + qml.RX(theta, wires=[0]) + qml.RX(phi, wires=[1]) + qml.expval(qml.PauliX(0)) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_identity_expectation(self, theta, phi, dev, tol): + """Tests identity.""" + + tape = qml.tape.QuantumScript( + [qml.RX(theta, wires=[0]), qml.RX(phi, wires=[1]), qml.CNOT(wires=[0, 1])], + [qml.expval(qml.Identity(wires=[0])), qml.expval(qml.Identity(wires=[1]))], + ) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_multi_wire_identity_expectation(self, theta, phi, dev, tol): + """Tests multi-wire identity.""" + + tape = qml.tape.QuantumScript( + [qml.RX(theta, wires=[0]), qml.RX(phi, wires=[1]), qml.CNOT(wires=[0, 1])], + [qml.expval(qml.Identity(wires=[0, 1]))], + ) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_PauliZ_expectation(self, theta, phi, dev, tol): + """Tests PauliZ.""" + + tape = qml.tape.QuantumScript( + [qml.RX(theta, wires=[0]), qml.RX(phi, wires=[1]), qml.CNOT(wires=[0, 1])], + [qml.expval(qml.PauliZ(wires=[0])), qml.expval(qml.PauliZ(wires=[1]))], + ) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_PauliX_expectation(self, theta, phi, dev, tol): + """Tests PauliX.""" + + tape = qml.tape.QuantumScript( + [qml.RY(theta, wires=[0]), qml.RY(phi, wires=[1]), qml.CNOT(wires=[0, 1])], + [qml.expval(qml.PauliX(wires=[0])), qml.expval(qml.PauliX(wires=[1]))], + ) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_PauliY_expectation(self, theta, phi, dev, tol): + """Tests PauliY.""" + + tape = qml.tape.QuantumScript( + [qml.RX(theta, wires=[0]), qml.RX(phi, wires=[1]), qml.CNOT(wires=[0, 1])], + [qml.expval(qml.PauliY(wires=[0])), qml.expval(qml.PauliY(wires=[1]))], + ) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_hadamard_expectation(self, theta, phi, dev, tol): + """Tests Hadamard.""" + + tape = qml.tape.QuantumScript( + [qml.RY(theta, wires=[0]), qml.RY(phi, wires=[1]), qml.CNOT(wires=[0, 1])], + [qml.expval(qml.Hadamard(wires=[0])), qml.expval(qml.Hadamard(wires=[1]))], + ) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_hermitian_expectation(self, theta, phi, dev, tol): + """Tests an Hermitian operator.""" + + with qml.tape.QuantumTape() as tape: + qml.RX(theta, wires=0) + qml.RX(phi, wires=1) + qml.RX(theta + phi, wires=2) + + for idx in range(3): + qml.expval(qml.Hermitian([[1, 0], [0, -1]], wires=[idx])) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_hamiltonian_expectation(self, theta, phi, dev, tol): + """Tests a Hamiltonian.""" + + ham = qml.Hamiltonian( + [1.0, 0.3, 0.3, 0.4], + [ + qml.PauliX(0) @ qml.PauliX(1), + qml.PauliZ(0), + qml.PauliZ(1), + qml.PauliX(0) @ qml.PauliY(1), + ], + ) + + with qml.tape.QuantumTape() as tape: + qml.RX(theta, wires=0) + qml.RX(phi, wires=1) + qml.RX(theta + phi, wires=2) + + qml.expval(ham) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_sparse_hamiltonian_expectation(self, theta, phi, dev, tol): + """Tests a Hamiltonian.""" + + ham = qml.SparseHamiltonian( + qml.Hamiltonian( + [1.0, 0.3, 0.3, 0.4], + [ + qml.PauliX(0) @ qml.PauliX(1), + qml.PauliZ(0), + qml.PauliZ(1), + qml.PauliX(0) @ qml.PauliY(1), + ], + ).sparse_matrix(), + wires=[0, 1], + ) + + with qml.tape.QuantumTape() as tape: + qml.RX(theta, wires=0) + qml.RX(phi, wires=1) + + qml.expval(ham) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + +@pytest.mark.parametrize("phi", PHI) +class TestOperatorArithmetic: + """Test integration with SProd, Prod, and Sum.""" + + @pytest.fixture(params=[np.complex64, np.complex128]) + def dev(self, request): + return LightningQubit(c_dtype=request.param) + + @staticmethod + def calculate_reference(tape): + dev = DefaultQubit(max_workers=1) + program, _ = dev.preprocess() + tapes, transf_fn = program([tape]) + results = dev.execute(tapes) + return transf_fn(results) + + @staticmethod + def process_and_execute(dev, tape): + program, _ = dev.preprocess() + tapes, transf_fn = program([tape]) + results = dev.execute(tapes) + return transf_fn(results) + + def test_s_prod(self, phi, dev, tol): + """Tests the `SProd` class.""" + + tape = qml.tape.QuantumScript( + [qml.RX(phi, wires=[0])], + [qml.expval(qml.s_prod(0.5, qml.PauliZ(0)))], + ) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_prod(self, phi, dev, tol): + """Tests the `Prod` class.""" + + tape = qml.tape.QuantumScript( + [qml.RX(phi, wires=[0]), qml.Hadamard(wires=[1]), qml.PauliZ(wires=[1])], + [qml.expval(qml.prod(qml.PauliZ(0), qml.PauliX(1)))], + ) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_sum(self, phi, dev, tol): + """Tests the `Sum` class.""" + tape = qml.tape.QuantumScript( + [qml.RX(phi, wires=[0]), qml.RX(-1.1 * phi, wires=[0])], + [qml.expval(qml.sum(qml.PauliZ(0), qml.PauliX(1)))], + ) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_integration(self, phi, dev, tol): + """Test a Combination of `Sum`, `SProd`, and `Prod`.""" + + obs = qml.sum(qml.s_prod(2.3, qml.PauliZ(0)), -0.5 * qml.prod(qml.PauliY(0), qml.PauliZ(1))) + + tape = qml.tape.QuantumScript( + [qml.RX(phi, wires=[0]), qml.RX(-1.1 * phi, wires=[0])], + [qml.expval(obs)], + ) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + +@pytest.mark.parametrize("theta, phi, varphi", list(zip(THETA, PHI, VARPHI))) +class TestTensorExpval: + """Test tensor expectation values""" + + @pytest.fixture(params=[np.complex64, np.complex128]) + def dev(self, request): + return LightningQubit(c_dtype=request.param) + + @staticmethod + def calculate_reference(tape): + dev = DefaultQubit(max_workers=1) + program, _ = dev.preprocess() + tapes, transf_fn = program([tape]) + results = dev.execute(tapes) + return transf_fn(results) + + @staticmethod + def process_and_execute(dev, tape): + program, _ = dev.preprocess() + tapes, transf_fn = program([tape]) + results = dev.execute(tapes) + return transf_fn(results) + + def test_PauliX_PauliY(self, theta, phi, varphi, dev, tol): + """Tests a tensor product involving PauliX and PauliY.""" + + with qml.tape.QuantumTape() as tape: + qml.RX(theta, wires=[0]) + qml.RX(phi, wires=[1]) + qml.RX(varphi, wires=[2]) + qml.CNOT(wires=[0, 1]) + qml.CNOT(wires=[1, 2]) + qml.expval(qml.PauliX(0) @ qml.PauliY(2)) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_PauliZ_identity(self, theta, phi, varphi, dev, tol): + """Tests a tensor product involving PauliZ and Identity.""" + + with qml.tape.QuantumTape() as tape: + qml.Identity(wires=[0]) + qml.RX(theta, wires=[0]) + qml.RX(phi, wires=[1]) + qml.RX(varphi, wires=[2]) + qml.CNOT(wires=[0, 1]) + qml.CNOT(wires=[1, 2]) + qml.expval(qml.PauliZ(0) @ qml.Identity(1) @ qml.PauliZ(2)) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_PauliZ_hadamard_PauliY(self, theta, phi, varphi, dev, tol): + """Tests a tensor product involving PauliY, PauliZ and Hadamard.""" + + with qml.tape.QuantumTape() as tape: + qml.RX(theta, wires=[0]) + qml.RX(phi, wires=[1]) + qml.RX(varphi, wires=[2]) + qml.CNOT(wires=[0, 1]) + qml.CNOT(wires=[1, 2]) + qml.expval(qml.PauliZ(0) @ qml.Hadamard(1) @ qml.PauliY(2)) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) diff --git a/tests/lightning_qubit2/test_serialize_2.py b/tests/lightning_qubit2/test_serialize_2.py new file mode 100644 index 0000000000..b957ac8153 --- /dev/null +++ b/tests/lightning_qubit2/test_serialize_2.py @@ -0,0 +1,596 @@ +# Copyright 2018-2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +Unit tests for the serialization helper functions. +""" +import pytest +from conftest import device_name, LightningDevice + +import numpy as np +import pennylane as qml +from pennylane_lightning.lightning_qubit._serialize import QuantumScriptSerializer + +# from pennylane_lightning.lightning_qubit._serialize_old import QuantumScriptSerializer + +if not LightningDevice._CPP_BINARY_AVAILABLE: + pytest.skip("No binary module found. Skipping.", allow_module_level=True) + +if device_name == "lightning.kokkos": + from pennylane_lightning.lightning_kokkos_ops.observables import ( + NamedObsC64, + NamedObsC128, + HermitianObsC64, + HermitianObsC128, + TensorProdObsC64, + TensorProdObsC128, + HamiltonianC64, + HamiltonianC128, + SparseHamiltonianC64, + SparseHamiltonianC128, + ) +elif device_name == "lightning.gpu": + from pennylane_lightning.lightning_gpu_ops.observables import ( + NamedObsC64, + NamedObsC128, + HermitianObsC64, + HermitianObsC128, + TensorProdObsC64, + TensorProdObsC128, + HamiltonianC64, + HamiltonianC128, + SparseHamiltonianC64, + SparseHamiltonianC128, + ) +else: + from pennylane_lightning.lightning_qubit_ops.observables import ( + NamedObsC64, + NamedObsC128, + HermitianObsC64, + HermitianObsC128, + TensorProdObsC64, + TensorProdObsC128, + HamiltonianC64, + HamiltonianC128, + SparseHamiltonianC64, + SparseHamiltonianC128, + ) + + +def test_wrong_device_name(): + """Test the device name is not a valid option""" + + with pytest.raises(qml.DeviceError, match="The device name"): + QuantumScriptSerializer("thunder.qubit") + + +@pytest.mark.parametrize( + "obs,obs_type", + [ + (qml.PauliZ(0), NamedObsC128), + (qml.PauliZ(0) @ qml.PauliZ(1), TensorProdObsC128), + (qml.Hadamard(0), NamedObsC128), + (qml.Hermitian(np.eye(2), wires=0), HermitianObsC128), + ( + qml.PauliZ(0) @ qml.Hadamard(1) @ (0.1 * (qml.PauliZ(2) + qml.PauliX(3))), + HamiltonianC128, + ), + ( + ( + qml.Hermitian(np.eye(2), wires=0) + @ qml.Hermitian(np.eye(2), wires=1) + @ qml.Projector([0], wires=2) + ), + TensorProdObsC128, + ), + ( + qml.PauliZ(0) @ qml.Hermitian(np.eye(2), wires=1) @ qml.Projector([0], wires=2), + TensorProdObsC128, + ), + (qml.Projector([0], wires=0), HermitianObsC128), + (qml.Hamiltonian([1], [qml.PauliZ(0)]), HamiltonianC128), + (qml.sum(qml.Hadamard(0), qml.PauliX(1)), HermitianObsC128), + ( + qml.SparseHamiltonian(qml.Hamiltonian([1], [qml.PauliZ(0)]).sparse_matrix(), wires=[0]), + SparseHamiltonianC128, + ), + ], +) +def test_obs_returns_expected_type(obs, obs_type): + """Tests that observables get serialized to the expected type.""" + assert isinstance(QuantumScriptSerializer(device_name)._ob(obs), obs_type) + + +class TestSerializeObs: + """Tests for the serialize_observables function""" + + @pytest.mark.parametrize("use_csingle", [True, False]) + def test_tensor_non_tensor_return(self, use_csingle): + """Test expected serialization for a mixture of tensor product and non-tensor product + return""" + with qml.tape.QuantumTape() as tape: + qml.expval(qml.PauliZ(0) @ qml.PauliX(1)) + qml.expval(qml.Hadamard(1)) + + tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 + named_obs = NamedObsC64 if use_csingle else NamedObsC128 + + s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) + + s_expected = [ + tensor_prod_obs([named_obs("PauliZ", [0]), named_obs("PauliX", [1])]), + named_obs("Hadamard", [1]), + ] + + assert s == s_expected + + @pytest.mark.parametrize("use_csingle", [True, False]) + def test_hermitian_return(self, use_csingle): + """Test expected serialization for a Hermitian return""" + with qml.tape.QuantumTape() as tape: + qml.expval(qml.Hermitian(np.eye(4), wires=[0, 1])) + + hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 + c_dtype = np.complex64 if use_csingle else np.complex128 + + s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) + s_expected = hermitian_obs( + np.array( + [1.0, 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, 1.0], + dtype=c_dtype, + ), + [0, 1], + ) + assert s[0] == s_expected + + @pytest.mark.parametrize("use_csingle", [True, False]) + def test_hermitian_tensor_return(self, use_csingle): + """Test expected serialization for a Hermitian return""" + with qml.tape.QuantumTape() as tape: + qml.expval(qml.Hermitian(np.eye(4), wires=[0, 1]) @ qml.Hermitian(np.eye(2), wires=[2])) + + c_dtype = np.complex64 if use_csingle else np.complex128 + tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 + hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 + s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) + + s_expected = tensor_prod_obs( + [ + hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), + hermitian_obs(np.eye(2, dtype=c_dtype).ravel(), [2]), + ] + ) + + assert s[0] == s_expected + + @pytest.mark.parametrize("use_csingle", [True, False]) + def test_mixed_tensor_return(self, use_csingle): + """Test expected serialization for a mixture of Hermitian and Pauli return""" + with qml.tape.QuantumTape() as tape: + qml.expval(qml.Hermitian(np.eye(4), wires=[0, 1]) @ qml.PauliY(2)) + + c_dtype = np.complex64 if use_csingle else np.complex128 + tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 + hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 + named_obs = NamedObsC64 if use_csingle else NamedObsC128 + + s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) + + s_expected = tensor_prod_obs( + [hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), named_obs("PauliY", [2])] + ) + + assert s[0] == s_expected + + @pytest.mark.parametrize("use_csingle", [True, False]) + def test_hamiltonian_return(self, use_csingle): + """Test expected serialization for a Hamiltonian return""" + + ham = qml.Hamiltonian( + [0.3, 0.5, 0.4], + [ + qml.Hermitian(np.eye(4), wires=[0, 1]) @ qml.PauliY(2), + qml.PauliX(0) @ qml.PauliY(2), + qml.Hermitian(np.ones((8, 8)), wires=range(3)), + ], + ) + + with qml.tape.QuantumTape() as tape: + qml.expval(ham) + + hamiltonian_obs = HamiltonianC64 if use_csingle else HamiltonianC128 + named_obs = NamedObsC64 if use_csingle else NamedObsC128 + hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 + tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 + r_dtype = np.float32 if use_csingle else np.float64 + c_dtype = np.complex64 if use_csingle else np.complex128 + + s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) + + s_expected = hamiltonian_obs( + np.array([0.3, 0.5, 0.4], dtype=r_dtype), + [ + tensor_prod_obs( + [ + hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), + named_obs("PauliY", [2]), + ] + ), + tensor_prod_obs([named_obs("PauliX", [0]), named_obs("PauliY", [2])]), + hermitian_obs(np.ones(64, dtype=c_dtype), [0, 1, 2]), + ], + ) + + assert s[0] == s_expected + + @pytest.mark.parametrize("use_csingle", [True, False]) + def test_hamiltonian_tensor_return(self, use_csingle): + """Test expected serialization for a Hamiltonian return""" + + with qml.tape.QuantumTape() as tape: + ham = qml.Hamiltonian( + [0.3, 0.5, 0.4], + [ + qml.Hermitian(np.eye(4), wires=[0, 1]) @ qml.PauliY(2), + qml.PauliX(0) @ qml.PauliY(2), + qml.Hermitian(np.ones((8, 8)), wires=range(3)), + ], + ) + qml.expval(ham @ qml.PauliZ(3)) + + hamiltonian_obs = HamiltonianC64 if use_csingle else HamiltonianC128 + named_obs = NamedObsC64 if use_csingle else NamedObsC128 + hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 + tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 + r_dtype = np.float32 if use_csingle else np.float64 + c_dtype = np.complex64 if use_csingle else np.complex128 + + s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) + + # Expression (ham @ obs) is converted internally by Pennylane + # where obs is appended to each term of the ham + s_expected = hamiltonian_obs( + np.array([0.3, 0.5, 0.4], dtype=r_dtype), + [ + tensor_prod_obs( + [ + hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), + named_obs("PauliY", [2]), + named_obs("PauliZ", [3]), + ] + ), + tensor_prod_obs( + [named_obs("PauliX", [0]), named_obs("PauliY", [2]), named_obs("PauliZ", [3])] + ), + tensor_prod_obs( + [hermitian_obs(np.ones(64, dtype=c_dtype), [0, 1, 2]), named_obs("PauliZ", [3])] + ), + ], + ) + + assert s[0] == s_expected + + @pytest.mark.parametrize("use_csingle", [True, False]) + def test_hamiltonian_mix_return(self, use_csingle): + """Test expected serialization for a Hamiltonian return""" + + ham1 = qml.Hamiltonian( + [0.3, 0.5, 0.4], + [ + qml.Hermitian(np.eye(4), wires=[0, 1]) @ qml.PauliY(2), + qml.PauliX(0) @ qml.PauliY(2), + qml.Hermitian(np.ones((8, 8)), wires=range(3)), + ], + ) + ham2 = qml.Hamiltonian( + [0.7, 0.3], + [qml.PauliX(0) @ qml.Hermitian(np.eye(4), wires=[1, 2]), qml.PauliY(0) @ qml.PauliX(2)], + ) + + with qml.tape.QuantumTape() as tape: + qml.expval(ham1) + qml.expval(ham2) + + hamiltonian_obs = HamiltonianC64 if use_csingle else HamiltonianC128 + named_obs = NamedObsC64 if use_csingle else NamedObsC128 + hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 + tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 + r_dtype = np.float32 if use_csingle else np.float64 + c_dtype = np.complex64 if use_csingle else np.complex128 + + s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) + + s_expected1 = hamiltonian_obs( + np.array([0.3, 0.5, 0.4], dtype=r_dtype), + [ + tensor_prod_obs( + [ + hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), + named_obs("PauliY", [2]), + ] + ), + tensor_prod_obs([named_obs("PauliX", [0]), named_obs("PauliY", [2])]), + hermitian_obs(np.ones(64, dtype=c_dtype), [0, 1, 2]), + ], + ) + s_expected2 = hamiltonian_obs( + np.array([0.7, 0.3], dtype=r_dtype), + [ + tensor_prod_obs( + [ + named_obs("PauliX", [0]), + hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [1, 2]), + ] + ), + tensor_prod_obs([named_obs("PauliY", [0]), named_obs("PauliX", [2])]), + ], + ) + + assert s[0] == s_expected1 + assert s[1] == s_expected2 + + @pytest.mark.parametrize( + "obs,coeffs,terms", + [ + (qml.prod(qml.PauliZ(0), qml.PauliX(1)), [1], [[("PauliX", 1), ("PauliZ", 0)]]), + (qml.s_prod(0.1, qml.PauliX(0)), [0.1], ("PauliX", 0)), + ( + qml.sum( + 0.5 * qml.prod(qml.PauliX(0), qml.PauliZ(1)), + 0.1 * qml.prod(qml.PauliZ(0), qml.PauliY(1)), + ), + [0.5, 0.1], + [[("PauliZ", 1), ("PauliX", 0)], [("PauliY", 1), ("PauliZ", 0)]], + ), + ], + ) + @pytest.mark.parametrize("use_csingle", [True, False]) + def test_op_arithmetic_uses_hamiltonian(self, use_csingle, obs, coeffs, terms): + """Tests that an arithmetic obs with a PauliRep serializes as a Hamiltonian.""" + tape = qml.tape.QuantumTape(measurements=[qml.expval(obs)]) + res, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) + assert len(res) == 1 + assert isinstance(res[0], HamiltonianC64 if use_csingle else HamiltonianC128) + + hamiltonian_obs = HamiltonianC64 if use_csingle else HamiltonianC128 + tensor_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 + named_obs = NamedObsC64 if use_csingle else NamedObsC128 + rtype = np.float32 if use_csingle else np.float64 + term_shape = np.array(terms).shape + + if len(term_shape) == 1: # just a single pauli op + expected_terms = [named_obs(terms[0], [terms[1]])] + elif len(term_shape) == 3: # list of tensor products + expected_terms = [ + tensor_obs([named_obs(pauli, [wire]) for pauli, wire in term]) for term in terms + ] + + coeffs = np.array(coeffs).astype(rtype) + assert res[0] == hamiltonian_obs(coeffs, expected_terms) + + @pytest.mark.parametrize("use_csingle", [True, False]) + def test_multi_wire_identity(self, use_csingle): + """Tests that multi-wire Identity does not fail serialization.""" + tape = qml.tape.QuantumTape(measurements=[qml.expval(qml.Identity(wires=[1, 2]))]) + res, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) + assert len(res) == 1 + + named_obs = NamedObsC64 if use_csingle else NamedObsC128 + assert res[0] == named_obs("Identity", [1]) + + +class TestSerializeOps: + """Tests for the _ops function""" + + def test_basic_circuit(self): + """Test expected serialization for a simple circuit""" + with qml.tape.QuantumTape() as tape: + qml.RX(0.4, wires=0) + qml.RY(0.6, wires=1) + qml.CNOT(wires=[0, 1]) + + s = QuantumScriptSerializer(device_name).serialize_ops(tape) + s_expected = ( + ( + ["RX", "RY", "CNOT"], + [np.array([0.4]), np.array([0.6]), []], + [[0], [1], [0, 1]], + [False, False, False], + [[], [], []], + [[], [], []], + [[], [], []], + ), + False, + ) + assert s == s_expected + + def test_basic_circuit_not_implemented_ctrl_ops(self): + """Test expected serialization for a simple circuit""" + ops = qml.OrbitalRotation(0.1234, wires=range(4)) + with qml.tape.QuantumTape() as tape: + qml.RX(0.4, wires=0) + qml.RY(0.6, wires=1) + qml.ctrl(ops, [4, 5]) + + s = QuantumScriptSerializer(device_name).serialize_ops(tape) + s_expected = ( + ( + ["RX", "RY", "QubitUnitary"], + [np.array([0.4]), np.array([0.6]), [0.0]], + [[0], [1], list(ops.wires)], + [False, False, False], + [[], [], [qml.matrix(ops)]], + [[], [], [4, 5]], + ), + False, + ) + assert s[0][0] == s_expected[0][0] + assert s[0][1] == s_expected[0][1] + assert s[0][2] == s_expected[0][2] + assert s[0][3] == s_expected[0][3] + assert all(np.allclose(s0, s1) for s0, s1 in zip(s[0][4], s_expected[0][4])) + assert s[0][5] == s_expected[0][5] + assert s[1] == s_expected[1] + + def test_multicontrolledx(self): + """Test expected serialization for a simple circuit""" + with qml.tape.QuantumTape() as tape: + qml.RX(0.4, wires=0) + qml.RY(0.6, wires=1) + qml.ctrl(qml.PauliX(wires=0), [1, 2, 3], control_values=[True, False, False]) + + s = QuantumScriptSerializer(device_name).serialize_ops(tape) + s_expected = ( + ( + ["RX", "RY", "PauliX"], + [np.array([0.4]), np.array([0.6]), []], + [[0], [1], [0]], + [False, False, False], + [[], [], []], + [[], [], [1, 2, 3]], + [[], [], [True, False, False]], + ), + False, + ) + assert s == s_expected + + @pytest.mark.parametrize("stateprep", [qml.QubitStateVector, qml.StatePrep]) + def test_skips_prep_circuit(self, stateprep): + """Test expected serialization for a simple circuit with state preparation, such that + the state preparation is skipped""" + with qml.tape.QuantumTape() as tape: + stateprep([1, 0], wires=0) + qml.BasisState([1], wires=1) + qml.RX(0.4, wires=0) + qml.RY(0.6, wires=1) + qml.CNOT(wires=[0, 1]) + + s = QuantumScriptSerializer(device_name).serialize_ops(tape) + s_expected = ( + ( + ["RX", "RY", "CNOT"], + [[0.4], [0.6], []], + [[0], [1], [0, 1]], + [False, False, False], + [[], [], []], + [[], [], []], + [[], [], []], + ), + True, + ) + assert s == s_expected + + def test_unsupported_kernel_circuit(self): + """Test expected serialization for a circuit including gates that do not have a dedicated + kernel""" + with qml.tape.QuantumTape() as tape: + qml.CNOT(wires=[0, 1]) + qml.RZ(0.2, wires=2) + + s = QuantumScriptSerializer(device_name).serialize_ops(tape) + s_expected = ( + ( + ["CNOT", "RZ"], + [[], [0.2]], + [[0, 1], [2]], + [False, False], + ), + False, + ) + assert s[0][0] == s_expected[0][0] + assert s[0][1] == s_expected[0][1] + + # Are we still supporting custom wires??? + # def test_custom_wires_circuit(self): + # """Test expected serialization for a simple circuit with custom wire labels""" + # with qml.tape.QuantumTape() as tape: + # qml.RX(0.4, wires="a") + # qml.RY(0.6, wires=3.2) + # qml.CNOT(wires=["a", 3.2]) + # qml.SingleExcitation(0.5, wires=["a", 3.2]) + # qml.SingleExcitationPlus(0.4, wires=["a", 3.2]) + # qml.adjoint(qml.SingleExcitationMinus(0.5, wires=["a", 3.2]), lazy=False) + + # s = QuantumScriptSerializer(device_name).serialize_ops(tape) + # s_expected = ( + # ( + # [ + # "RX", + # "RY", + # "CNOT", + # "SingleExcitation", + # "SingleExcitationPlus", + # "SingleExcitationMinus", + # ], + # [[0.4], [0.6], [], [0.5], [0.4], [-0.5]], + # [[0], [1], [0, 1], [0, 1], [0, 1], [0, 1]], + # [False, False, False, False, False, False], + # [[], [], [], [], [], []], + # [[], [], [], [], [], []], + # [[], [], [], [], [], []], + # ), + # False, + # ) + # assert s == s_expected + + @pytest.mark.parametrize("C", [True, False]) + def test_integration(self, C): + """Test expected serialization for a random circuit""" + with qml.tape.QuantumTape() as tape: + qml.RX(0.4, wires=0) + qml.RY(0.6, wires=1) + qml.CNOT(wires=[0, 1]) + qml.QubitUnitary(np.eye(4), wires=[0, 1]) + qml.templates.QFT(wires=[0, 1, 2]) + qml.DoubleExcitation(0.555, wires=[3, 2, 1, 0]) + qml.DoubleExcitationMinus(0.555, wires=[0, 1, 2, 3]) + qml.DoubleExcitationPlus(0.555, wires=[0, 1, 2, 3]) + + s = QuantumScriptSerializer(device_name).serialize_ops(tape) + + dtype = np.complex64 if C else np.complex128 + s_expected = ( + ( + [ + "RX", + "RY", + "CNOT", + "QubitUnitary", + "QFT", + "DoubleExcitation", + "DoubleExcitationMinus", + "DoubleExcitationPlus", + ], + [[0.4], [0.6], [], [0.0], [], [0.555], [0.555], [0.555]], + [[0], [1], [0, 1], [0, 1], [0, 1, 2], [3, 2, 1, 0], [0, 1, 2, 3], [0, 1, 2, 3]], + [False, False, False, False, False, False, False, False], + [ + [], + [], + [], + qml.matrix(qml.QubitUnitary(np.eye(4, dtype=dtype), wires=[0, 1])), + qml.matrix(qml.templates.QFT(wires=[0, 1, 2])), + [], + [], + [], + ], + ), + False, + ) + assert s[0][0] == s_expected[0][0] + assert s[0][1] == s_expected[0][1] + assert s[0][2] == s_expected[0][2] + assert s[0][3] == s_expected[0][3] + assert s[1] == s_expected[1] + + assert all(np.allclose(s1, s2) for s1, s2 in zip(s[0][4], s_expected[0][4])) From 0460ae277362ad78f61cf9739e17eaa2e95e3917 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Tue, 13 Feb 2024 09:44:57 -0500 Subject: [PATCH 013/428] update CI --- .github/workflows/tests_linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests_linux.yml b/.github/workflows/tests_linux.yml index 71aa8284b9..28b4d616ca 100644 --- a/.github/workflows/tests_linux.yml +++ b/.github/workflows/tests_linux.yml @@ -243,7 +243,7 @@ jobs: run: | cd main/ DEVICENAME=`echo ${{ matrix.pl_backend }} | sed "s/_/./g"` - PL_DEVICE=${DEVICENAME} python -m pytest pytest tests/lightning_qubit2/ + PL_DEVICE=${DEVICENAME} python -m pytest tests/lightning_qubit2/ cpptestswithOpenBLAS: if: ${{ !contains(fromJSON('["schedule", "workflow_dispatch"]'), github.event_name) }} From 1358706222c2cb060a59c03e003944f67a8a6346 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Tue, 13 Feb 2024 09:47:17 -0500 Subject: [PATCH 014/428] update CI --- .github/workflows/tests_linux.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/tests_linux.yml b/.github/workflows/tests_linux.yml index 28b4d616ca..1e521ec5bb 100644 --- a/.github/workflows/tests_linux.yml +++ b/.github/workflows/tests_linux.yml @@ -236,8 +236,7 @@ jobs: - name: Install backend device run: | cd main - CMAKE_ARGS="-DPL_BACKEND=${{ matrix.pl_backend }} -DLQ_ENABLE_KERNEL_OMP=ON -DENABLE_PYTHON=ON -DCMAKE_CXX_COMPILER=$(which g++-$GCC_VERSION)" \ - python -m pip install -e . -vv + PL_BACKEND=lightning.qubit pip install -e . -vv - name: Run PennyLane-Lightning unit tests run: | From 8c414db87711f6e512477baf9af3d15d0b209edf Mon Sep 17 00:00:00 2001 From: albi3ro Date: Tue, 13 Feb 2024 10:51:34 -0500 Subject: [PATCH 015/428] add wire mapping, black --- .../lightning_qubit/lightning_qubit2.py | 258 ++++++++++-------- 1 file changed, 143 insertions(+), 115 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index 30cabef7df..e52190ffa0 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -21,135 +21,160 @@ import pennylane as qml from pennylane.devices import Device, ExecutionConfig, DefaultExecutionConfig -from pennylane.devices.preprocess import decompose, validate_device_wires, decompose, validate_measurements, validate_observables, no_sampling +from pennylane.devices.preprocess import ( + decompose, + validate_device_wires, + decompose, + validate_measurements, + validate_observables, + no_sampling, +) from pennylane.devices.qubit.sampling import get_num_shots_and_executions from pennylane.tape import QuantumScript from pennylane.transforms.core import TransformProgram from pennylane.typing import Result, ResultBatch -def dummy_simulate(circuit, rng=None, c_dtype=np.complex128, batch_obs=False, mcmc=False, - kernel_name="Local", num_burnin=100): +def dummy_simulate( + circuit, + rng=None, + c_dtype=np.complex128, + batch_obs=False, + mcmc=False, + kernel_name="Local", + num_burnin=100, +): return tuple(0.0 for _ in circuit.measurements) + def dummy_jacobian(circuit): return np.array(0.0) + def dummy_simulate_and_jacobian(circuit): return np.array(0.0), np.array(0.0) + Result_or_ResultBatch = Union[Result, ResultBatch] QuantumTapeBatch = Sequence[qml.tape.QuantumTape] QuantumTape_or_Batch = Union[qml.tape.QuantumTape, QuantumTapeBatch] -_operations = frozenset({ - "Identity", - "BasisState", - "QubitStateVector", - "StatePrep", - "QubitUnitary", - "ControlledQubitUnitary", - "MultiControlledX", - "DiagonalQubitUnitary", - "PauliX", - "PauliY", - "PauliZ", - "MultiRZ", - "Hadamard", - "S", - "Adjoint(S)", - "T", - "Adjoint(T)", - "SX", - "Adjoint(SX)", - "CNOT", - "SWAP", - "ISWAP", - "PSWAP", - "Adjoint(ISWAP)", - "SISWAP", - "Adjoint(SISWAP)", - "SQISW", - "CSWAP", - "Toffoli", - "CY", - "CZ", - "PhaseShift", - "ControlledPhaseShift", - "CPhase", - "RX", - "RY", - "RZ", - "Rot", - "CRX", - "CRY", - "CRZ", - "C(PauliX)", - "C(PauliY)", - "C(PauliZ)", - "C(Hadamard)", - "C(S)", - "C(T)", - "C(PhaseShift)", - "C(RX)", - "C(RY)", - "C(RZ)", - "C(SWAP)", - "C(IsingXX)", - "C(IsingXY)", - "C(IsingYY)", - "C(IsingZZ)", - "C(SingleExcitation)", - "C(SingleExcitationMinus)", - "C(SingleExcitationPlus)", - "C(DoubleExcitation)", - "C(DoubleExcitationMinus)", - "C(DoubleExcitationPlus)", - "CRot", - "IsingXX", - "IsingYY", - "IsingZZ", - "IsingXY", - "SingleExcitation", - "SingleExcitationPlus", - "SingleExcitationMinus", - "DoubleExcitation", - "DoubleExcitationPlus", - "DoubleExcitationMinus", - "QubitCarry", - "QubitSum", - "OrbitalRotation", - "QFT", - "ECR", - "BlockEncode", -}) - -_observables = frozenset({ - "PauliX", - "PauliY", - "PauliZ", - "Hadamard", - "Hermitian", - "Identity", - "Projector", - "SparseHamiltonian", - "Hamiltonian", - "Sum", - "SProd", - "Prod", - "Exp", -}) +_operations = frozenset( + { + "Identity", + "BasisState", + "QubitStateVector", + "StatePrep", + "QubitUnitary", + "ControlledQubitUnitary", + "MultiControlledX", + "DiagonalQubitUnitary", + "PauliX", + "PauliY", + "PauliZ", + "MultiRZ", + "Hadamard", + "S", + "Adjoint(S)", + "T", + "Adjoint(T)", + "SX", + "Adjoint(SX)", + "CNOT", + "SWAP", + "ISWAP", + "PSWAP", + "Adjoint(ISWAP)", + "SISWAP", + "Adjoint(SISWAP)", + "SQISW", + "CSWAP", + "Toffoli", + "CY", + "CZ", + "PhaseShift", + "ControlledPhaseShift", + "CPhase", + "RX", + "RY", + "RZ", + "Rot", + "CRX", + "CRY", + "CRZ", + "C(PauliX)", + "C(PauliY)", + "C(PauliZ)", + "C(Hadamard)", + "C(S)", + "C(T)", + "C(PhaseShift)", + "C(RX)", + "C(RY)", + "C(RZ)", + "C(SWAP)", + "C(IsingXX)", + "C(IsingXY)", + "C(IsingYY)", + "C(IsingZZ)", + "C(SingleExcitation)", + "C(SingleExcitationMinus)", + "C(SingleExcitationPlus)", + "C(DoubleExcitation)", + "C(DoubleExcitationMinus)", + "C(DoubleExcitationPlus)", + "CRot", + "IsingXX", + "IsingYY", + "IsingZZ", + "IsingXY", + "SingleExcitation", + "SingleExcitationPlus", + "SingleExcitationMinus", + "DoubleExcitation", + "DoubleExcitationPlus", + "DoubleExcitationMinus", + "QubitCarry", + "QubitSum", + "OrbitalRotation", + "QFT", + "ECR", + "BlockEncode", + } +) + +_observables = frozenset( + { + "PauliX", + "PauliY", + "PauliZ", + "Hadamard", + "Hermitian", + "Identity", + "Projector", + "SparseHamiltonian", + "Hamiltonian", + "Sum", + "SProd", + "Prod", + "Exp", + } +) + def stopping_condition(op: qml.operation.Operator) -> bool: return op.name in _operations + def accepted_observables(obs: qml.operation.Operator) -> bool: return obs.name in _observables -def accepted_measurements(m : qml.measurements.MeasurementProcess) -> bool: + +def accepted_measurements(m: qml.measurements.MeasurementProcess) -> bool: return isinstance(m, (qml.measurements.ExpectationMP)) + class LightningQubit2(Device): """PennyLane Lightning Qubit device. @@ -182,7 +207,7 @@ class LightningQubit2(Device): qubit is built with OpenMP. """ - name = 'lightning.qubit2' + name = "lightning.qubit2" _device_options = ["rng", "c_dtype", "batch_obs", "mcmc", "kernel_name", "num_burnin"] @@ -224,17 +249,14 @@ def __init__( # pylint: disable=too-many-arguments @property def operation(self) -> frozenset[str]: - """The names of supported operations. - """ + """The names of supported operations.""" return _operations @property def observables(self) -> frozenset[str]: - """The names of supported observables. - """ + """The names of supported observables.""" return _observables - def _setup_execution_config(self, config): updated_values = {} if config.gradient_method == "best": @@ -251,7 +273,6 @@ def _setup_execution_config(self, config): return replace(config, **updated_values, device_options=new_device_options) - def supports_derivatives( self, execution_config: Optional[ExecutionConfig] = None, @@ -263,12 +284,16 @@ def supports_derivatives( return False if circuit is None: return True - return all(isinstance(m, qml.measurements.ExpectationMP) for m in circuit.measurements) and not circuit.shots - + return ( + all(isinstance(m, qml.measurements.ExpectationMP) for m in circuit.measurements) + and not circuit.shots + ) def preprocess(self, execution_config: ExecutionConfig = DefaultExecutionConfig): program = TransformProgram() - program.add_transform(validate_measurements, analytic_measurements=accepted_measurements, name=self.name) + program.add_transform( + validate_measurements, analytic_measurements=accepted_measurements, name=self.name + ) program.add_transform(no_sampling) program.add_transform(validate_observables, accepted_observables, name=self.name) program.add_transform(validate_device_wires, self.wires, name=self.name) @@ -295,7 +320,11 @@ def _execute_tracking(self, circuits): ) self.tracker.record() - def execute(self, circuits : QuantumTape_or_Batch, execution_config: ExecutionConfig = DefaultExecutionConfig) -> Result_or_ResultBatch: + def execute( + self, + circuits: QuantumTape_or_Batch, + execution_config: ExecutionConfig = DefaultExecutionConfig, + ) -> Result_or_ResultBatch: is_single_circuit = False if isinstance(circuits, QuantumScript): is_single_circuit = True @@ -306,11 +335,11 @@ def execute(self, circuits : QuantumTape_or_Batch, execution_config: ExecutionCo results = [] for circuit in circuits: + circuit = circuit.map_to_standard_wires() results.append(dummy_simulate(circuit, **execution_config.device_options)) return results[0] if is_single_circuit else tuple(results) - def compute_derivatives( self, circuits: QuantumTape_or_Batch, @@ -328,7 +357,6 @@ def compute_derivatives( return res[0] if is_single_circuit else res - def execute_and_compute_derivatives( self, circuits: QuantumTape_or_Batch, @@ -351,4 +379,4 @@ def execute_and_compute_derivatives( results = tuple(dummy_simulate_and_jacobian(c) for c in circuits) results, jacs = tuple(zip(*results)) - return (results[0], jacs[0]) if is_single_circuit else (results, jacs) \ No newline at end of file + return (results[0], jacs[0]) if is_single_circuit else (results, jacs) From 695f4b6b42709724964557af3f6e8409c8393541 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Tue, 13 Feb 2024 11:23:28 -0500 Subject: [PATCH 016/428] add tests for custom wires --- .../lightning_qubit/lightning_qubit2.py | 2 +- tests/lightning_qubit2/test_expval_2.py | 12 +++++-- tests/lightning_qubit2/test_serialize_2.py | 33 ------------------- 3 files changed, 11 insertions(+), 36 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index a751311066..a4a093862a 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -238,7 +238,7 @@ class LightningQubit2(Device): def __init__( # pylint: disable=too-many-arguments self, - wires=None, # the number of wires are always needed for Lightning. Do we have a better way to get it + wires=None, *, c_dtype=np.complex128, shots=None, diff --git a/tests/lightning_qubit2/test_expval_2.py b/tests/lightning_qubit2/test_expval_2.py index cb66797b3d..f7d44fbc3f 100644 --- a/tests/lightning_qubit2/test_expval_2.py +++ b/tests/lightning_qubit2/test_expval_2.py @@ -105,11 +105,19 @@ def test_multi_wire_identity_expectation(self, theta, phi, dev, tol): assert np.allclose(calculated_val, reference_val, tol) - def test_PauliZ_expectation(self, theta, phi, dev, tol): + @pytest.mark.parametrize( + "wires", + [ + ([0, 1]), + (["a", 1]), + (["b", "a"]), + ], + ) + def test_PauliZ_expectation(self, theta, phi, dev, tol, wires): """Tests PauliZ.""" tape = qml.tape.QuantumScript( - [qml.RX(theta, wires=[0]), qml.RX(phi, wires=[1]), qml.CNOT(wires=[0, 1])], + [qml.RX(theta, wires=wires[0]), qml.RX(phi, wires=[1]), qml.CNOT(wires=[0, 1])], [qml.expval(qml.PauliZ(wires=[0])), qml.expval(qml.PauliZ(wires=[1]))], ) diff --git a/tests/lightning_qubit2/test_serialize_2.py b/tests/lightning_qubit2/test_serialize_2.py index b957ac8153..7f14422c38 100644 --- a/tests/lightning_qubit2/test_serialize_2.py +++ b/tests/lightning_qubit2/test_serialize_2.py @@ -510,39 +510,6 @@ def test_unsupported_kernel_circuit(self): assert s[0][0] == s_expected[0][0] assert s[0][1] == s_expected[0][1] - # Are we still supporting custom wires??? - # def test_custom_wires_circuit(self): - # """Test expected serialization for a simple circuit with custom wire labels""" - # with qml.tape.QuantumTape() as tape: - # qml.RX(0.4, wires="a") - # qml.RY(0.6, wires=3.2) - # qml.CNOT(wires=["a", 3.2]) - # qml.SingleExcitation(0.5, wires=["a", 3.2]) - # qml.SingleExcitationPlus(0.4, wires=["a", 3.2]) - # qml.adjoint(qml.SingleExcitationMinus(0.5, wires=["a", 3.2]), lazy=False) - - # s = QuantumScriptSerializer(device_name).serialize_ops(tape) - # s_expected = ( - # ( - # [ - # "RX", - # "RY", - # "CNOT", - # "SingleExcitation", - # "SingleExcitationPlus", - # "SingleExcitationMinus", - # ], - # [[0.4], [0.6], [], [0.5], [0.4], [-0.5]], - # [[0], [1], [0, 1], [0, 1], [0, 1], [0, 1]], - # [False, False, False, False, False, False], - # [[], [], [], [], [], []], - # [[], [], [], [], [], []], - # [[], [], [], [], [], []], - # ), - # False, - # ) - # assert s == s_expected - @pytest.mark.parametrize("C", [True, False]) def test_integration(self, C): """Test expected serialization for a random circuit""" From 91777b30946f95d9620e71addeffc7d7c93e0f47 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Tue, 13 Feb 2024 11:26:22 -0500 Subject: [PATCH 017/428] add tests for custom wires --- tests/lightning_qubit2/test_expval_2.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/lightning_qubit2/test_expval_2.py b/tests/lightning_qubit2/test_expval_2.py index f7d44fbc3f..78553c3e44 100644 --- a/tests/lightning_qubit2/test_expval_2.py +++ b/tests/lightning_qubit2/test_expval_2.py @@ -117,8 +117,8 @@ def test_PauliZ_expectation(self, theta, phi, dev, tol, wires): """Tests PauliZ.""" tape = qml.tape.QuantumScript( - [qml.RX(theta, wires=wires[0]), qml.RX(phi, wires=[1]), qml.CNOT(wires=[0, 1])], - [qml.expval(qml.PauliZ(wires=[0])), qml.expval(qml.PauliZ(wires=[1]))], + [qml.RX(theta, wires=wires[0]), qml.RX(phi, wires=wires[1]), qml.CNOT(wires=wires)], + [qml.expval(qml.PauliZ(wires=wires[0])), qml.expval(qml.PauliZ(wires=wires[1]))], ) calculated_val = self.process_and_execute(dev, tape) From 6d98910cd9d069354b99352b8c6abff3c057f417 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Tue, 13 Feb 2024 13:35:36 -0500 Subject: [PATCH 018/428] add review suggestions --- .../lightning_qubit/_measurements.py | 14 +------ .../lightning_qubit/_state_vector.py | 41 ++++--------------- .../lightning_qubit/lightning_qubit2.py | 9 ++-- 3 files changed, 15 insertions(+), 49 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/_measurements.py b/pennylane_lightning/lightning_qubit/_measurements.py index a1de9000c9..ffc2a8912e 100644 --- a/pennylane_lightning/lightning_qubit/_measurements.py +++ b/pennylane_lightning/lightning_qubit/_measurements.py @@ -151,7 +151,7 @@ def measurement(self, measurementprocess: MeasurementProcess) -> TensorLike: """ return self.get_measurement_function(measurementprocess)(measurementprocess) - def measure_final_state(self, circuit: QuantumScript, is_state_batched) -> Result: + def measure_final_state(self, circuit: QuantumScript) -> Result: """ Perform the measurements required by the circuit on the provided state. @@ -159,24 +159,14 @@ def measure_final_state(self, circuit: QuantumScript, is_state_batched) -> Resul Args: circuit (QuantumScript): The single circuit to simulate - is_state_batched (bool): Whether the state has a batch dimension or not. Returns: Tuple[TensorLike]: The measurement results """ - if set(circuit.wires) != set(range(circuit.num_wires)): - wire_map = {w: i for i, w in enumerate(circuit.wires)} - circuit = qml.map_wires(circuit, wire_map) if not circuit.shots: # analytic case if len(circuit.measurements) == 1: return self.measurement(circuit.measurements[0]) - results = [] - for mp in circuit.measurements: - measure_val = self.measurement(mp) - results += [ - measure_val, - ] - return tuple(results) + return tuple(self.measurement(mp) for mp in circuit.measurements) diff --git a/pennylane_lightning/lightning_qubit/_state_vector.py b/pennylane_lightning/lightning_qubit/_state_vector.py index a4e4c0ca01..66c1d410b6 100644 --- a/pennylane_lightning/lightning_qubit/_state_vector.py +++ b/pennylane_lightning/lightning_qubit/_state_vector.py @@ -234,7 +234,6 @@ def _apply_state_vector(self, state, device_wires): ravelled_indices, state = self._preprocess_state_vector(state, device_wires) # translate to wire labels used by device - device_wires = self.map_wires(device_wires) output_shape = [2] * self.num_wires if len(device_wires) == self.num_wires and Wires(sorted(device_wires)) == device_wires: @@ -270,20 +269,13 @@ def _apply_lightning_controlled(self, operation): """ state = self.state_vector - basename = "PauliX" if operation.name == "MultiControlledX" else operation.base.name + basename = operation.base.name if basename == "Identity": return method = getattr(state, f"{basename}", None) - control_wires = self.wires.indices(operation.control_wires) - control_values = ( - [bool(int(i)) for i in operation.hyperparameters["control_values"]] - if operation.name == "MultiControlledX" - else operation.control_values - ) - if operation.name == "MultiControlledX": - target_wires = list(set(self.wires.indices(operation.wires)) - set(control_wires)) - else: - target_wires = self.wires.indices(operation.target_wires) + control_wires = list(operation.control_wires) + control_values = operation.control_values + target_wires = list(operation.target_wires) if method is not None: # apply n-controlled specialized gate inv = False param = operation.parameters @@ -321,16 +313,13 @@ def apply_lightning(self, operations): if name == "Identity": continue method = getattr(state, name, None) - wires = self.wires.indices(operation.wires) + wires = list(operation.wires) if method is not None: # apply specialized gate inv = False param = operation.parameters method(wires, inv, param) - elif ( - name[0:2] == "C(" or name == "ControlledQubitUnitary" or name == "MultiControlledX" - ): # apply n-controlled gate - print("hi") + elif isinstance(operation, qml.ops.Controlled): # apply n-controlled gate self._apply_lightning_controlled(operation) else: # apply gate as a matrix # Inverse can be set to False since qml.matrix(operation) is already in @@ -353,16 +342,9 @@ def apply_operations(self, operations): self._apply_basis_state(operations[0].parameters[0], operations[0].wires) operations = operations[1:] - for operation in operations: - if isinstance(operation, (StatePrep, BasisState)): - raise DeviceError( - f"Operation {operation.name} cannot be used after other " - f"Operations have already been applied on a {self.short_name} device." - ) - self.apply_lightning(operations) - def get_final_state(self, circuit: QuantumScript, debugger=None): + def get_final_state(self, circuit: QuantumScript): """ Get the final state that results from executing the given quantum script. @@ -370,17 +352,12 @@ def get_final_state(self, circuit: QuantumScript, debugger=None): Args: circuit (QuantumScript): The single circuit to simulate - debugger (._Debugger): The debugger to use Returns: Tuple: A tuple containing the Lightning final state handler of the quantum script and whether the state has a batch dimension. """ + self.apply_operations(circuit.operations) - circuit = circuit.map_to_standard_wires() - self.apply_operations(circuit._ops) - - # No batching support yet. - - return self, False # is_state_batched + return self diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index a4a093862a..077b4875e2 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -50,14 +50,13 @@ LQ_CPP_BINARY_AVAILABLE = False -def simulate(circuit: QuantumScript, dtype=np.complex128, debugger=None) -> Result: +def simulate(circuit: QuantumScript, dtype=np.complex128) -> Result: """Simulate a single quantum script. Args: circuit (QuantumTape): The single circuit to simulate dtype: Datatypes for state-vector representation. Must be one of ``np.complex64`` or ``np.complex128``. - debugger (_Debugger): The debugger to use Returns: tuple(TensorLike): The results of the simulation @@ -65,10 +64,10 @@ def simulate(circuit: QuantumScript, dtype=np.complex128, debugger=None) -> Resu Note that this function can return measurements for non-commuting observables simultaneously. """ - state, is_state_batched = LightningStateVector( + state = LightningStateVector( num_wires=circuit.num_wires, dtype=dtype - ).get_final_state(circuit, debugger=debugger) - return LightningMeasurements(state).measure_final_state(circuit, is_state_batched) + ).get_final_state(circuit) + return LightningMeasurements(state).measure_final_state(circuit) def dummy_jacobian(circuit: QuantumScript): From a4147e9aa5d3fe4542f4d43669e65225f3551ac5 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Tue, 13 Feb 2024 13:36:23 -0500 Subject: [PATCH 019/428] format --- pennylane_lightning/lightning_qubit/lightning_qubit2.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index 077b4875e2..de79f1d143 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -64,9 +64,7 @@ def simulate(circuit: QuantumScript, dtype=np.complex128) -> Result: Note that this function can return measurements for non-commuting observables simultaneously. """ - state = LightningStateVector( - num_wires=circuit.num_wires, dtype=dtype - ).get_final_state(circuit) + state = LightningStateVector(num_wires=circuit.num_wires, dtype=dtype).get_final_state(circuit) return LightningMeasurements(state).measure_final_state(circuit) From ba7d69918bcae3e498c48a8faec9a985318a2e2c Mon Sep 17 00:00:00 2001 From: albi3ro Date: Fri, 16 Feb 2024 13:46:56 -0500 Subject: [PATCH 020/428] update --- .../lightning_qubit/lightning_qubit2.py | 119 ++++++++---------- 1 file changed, 53 insertions(+), 66 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index e52190ffa0..cf32b7f178 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -19,8 +19,22 @@ from dataclasses import replace import numpy as np + +try: + # pylint: disable=import-error, no-name-in-module + from pennylane_lightning.lightning_qubit_ops import ( + StateVectorC64, + StateVectorC128, + ) + + LQ_CPP_BINARY_AVAILABLE = True + except ImportError: + LQ_CPP_BINARY_AVAILABLE = False + + import pennylane as qml from pennylane.devices import Device, ExecutionConfig, DefaultExecutionConfig +from pennylane.devices.modifiers import convert_single_circuit_to_batch, simulator_tracking from pennylane.devices.preprocess import ( decompose, validate_device_wires, @@ -33,9 +47,11 @@ from pennylane.tape import QuantumScript from pennylane.transforms.core import TransformProgram from pennylane.typing import Result, ResultBatch +from pennylane_lightning.lightning_qubit.device_modifiers import convert_single_circuit_to_batch +from .device_modifiers import convert_single_circuit_to_batch -def dummy_simulate( +def simulate( circuit, rng=None, c_dtype=np.complex128, @@ -44,14 +60,17 @@ def dummy_simulate( kernel_name="Local", num_burnin=100, ): - return tuple(0.0 for _ in circuit.measurements) + """Calculate the results for a given circuit.""" + return 0.0 -def dummy_jacobian(circuit): +def jacobian(circuit): + """Calculate the jacobian for a given circuit.""" return np.array(0.0) -def dummy_simulate_and_jacobian(circuit): +def simulate_and_jacobian(circuit): + """Calculate the results and jacobian for a single circuit.""" return np.array(0.0), np.array(0.0) @@ -143,6 +162,7 @@ def dummy_simulate_and_jacobian(circuit): "BlockEncode", } ) +"""The set of supported operations.""" _observables = frozenset( { @@ -161,20 +181,26 @@ def dummy_simulate_and_jacobian(circuit): "Exp", } ) +"""Test set of supported observables.""" def stopping_condition(op: qml.operation.Operator) -> bool: + """A function that determines whether or not an operation is supported by ``lightning.qubit``.""" return op.name in _operations def accepted_observables(obs: qml.operation.Operator) -> bool: + """A function that determines whether or not an observable is supported by ``lightning.qubit``.""" return obs.name in _observables -def accepted_measurements(m: qml.measurements.MeasurementProcess) -> bool: +def accepted_analytic_measurements(m: qml.measurements.MeasurementProcess) -> bool: + """Whether or not a state based measurement is supported by ``lightning.qubit``.""" return isinstance(m, (qml.measurements.ExpectationMP)) +@simulator_tracking +@convert_single_circuit_to_batch class LightningQubit2(Device): """PennyLane Lightning Qubit device. @@ -209,11 +235,11 @@ class LightningQubit2(Device): name = "lightning.qubit2" - _device_options = ["rng", "c_dtype", "batch_obs", "mcmc", "kernel_name", "num_burnin"] + _device_options = ("rng", "c_dtype", "batch_obs", "mcmc", "kernel_name", "num_burnin") def __init__( # pylint: disable=too-many-arguments self, - wires=None, + wires, *, c_dtype=np.complex128, shots=None, @@ -223,6 +249,10 @@ def __init__( # pylint: disable=too-many-arguments num_burnin=100, batch_obs=False, ): + if not LQ_CPP_BINARY_AVAILABLE: + raise ImportError("Pre-compiled binaries for lightning.qubit are not available. " + "To manually compile from source, follow the instructions at " + "https://pennylane-lightning.readthedocs.io/en/latest/installation.html.") super().__init__(wires=wires, shots=shots) seed = np.random.randint(0, high=10000000) if seed == "global" else seed self._rng = np.random.default_rng(seed) @@ -247,17 +277,25 @@ def __init__( # pylint: disable=too-many-arguments self._kernel_name = None self._num_burnin = None + @property + def c_dtype(self): + """State vector complex data type.""" + return self._c_dtype + @property def operation(self) -> frozenset[str]: - """The names of supported operations.""" + """The names of the supported operations.""" return _operations @property def observables(self) -> frozenset[str]: - """The names of supported observables.""" + """The names of the supported observables.""" return _observables def _setup_execution_config(self, config): + """ + Update the execution config with choices for how the device should be used and the device options. + """ updated_values = {} if config.gradient_method == "best": updated_values["gradient_method"] = "adjoint" @@ -292,7 +330,7 @@ def supports_derivatives( def preprocess(self, execution_config: ExecutionConfig = DefaultExecutionConfig): program = TransformProgram() program.add_transform( - validate_measurements, analytic_measurements=accepted_measurements, name=self.name + validate_measurements, analytic_measurements=accepted_analytic_measurements, name=self.name ) program.add_transform(no_sampling) program.add_transform(validate_observables, accepted_observables, name=self.name) @@ -302,81 +340,30 @@ def preprocess(self, execution_config: ExecutionConfig = DefaultExecutionConfig) program.add_transform(qml.transforms.broadcast_expand) return program, self._setup_execution_config(execution_config) - def _execute_tracking(self, circuits): - self.tracker.update(batches=1) - self.tracker.record() - for c in circuits: - qpu_executions, shots = get_num_shots_and_executions(c) - if c.shots: - self.tracker.update( - simulations=1, - executions=qpu_executions, - shots=shots, - ) - else: - self.tracker.update( - simulations=1, - executions=qpu_executions, - ) - self.tracker.record() - def execute( self, circuits: QuantumTape_or_Batch, execution_config: ExecutionConfig = DefaultExecutionConfig, ) -> Result_or_ResultBatch: - is_single_circuit = False - if isinstance(circuits, QuantumScript): - is_single_circuit = True - circuits = (circuits,) - - if self.tracker.active: - self._execute_tracking(circuits) results = [] for circuit in circuits: circuit = circuit.map_to_standard_wires() - results.append(dummy_simulate(circuit, **execution_config.device_options)) + results.append(simulate(circuit, **execution_config.device_options)) - return results[0] if is_single_circuit else tuple(results) + return tuple(results) def compute_derivatives( self, circuits: QuantumTape_or_Batch, execution_config: ExecutionConfig = DefaultExecutionConfig, ): - is_single_circuit = False - if isinstance(circuits, QuantumScript): - is_single_circuit = True - circuits = [circuits] - - if self.tracker.active: - self.tracker.update(derivative_batches=1, derivatives=len(circuits)) - self.tracker.record() - res = tuple(dummy_jacobian(circuit) for circuit in circuits) - - return res[0] if is_single_circuit else res + return tuple(jacobian(circuit) for circuit in circuits) def execute_and_compute_derivatives( self, circuits: QuantumTape_or_Batch, execution_config: ExecutionConfig = DefaultExecutionConfig, ): - is_single_circuit = False - if isinstance(circuits, QuantumScript): - is_single_circuit = True - circuits = [circuits] - - if self.tracker.active: - for c in circuits: - self.tracker.update(resources=c.specs["resources"]) - self.tracker.update( - execute_and_derivative_batches=1, - executions=len(circuits), - derivatives=len(circuits), - ) - self.tracker.record() - - results = tuple(dummy_simulate_and_jacobian(c) for c in circuits) - results, jacs = tuple(zip(*results)) - return (results[0], jacs[0]) if is_single_circuit else (results, jacs) + results = tuple(simulate_and_jacobian(c) for c in circuits) + return tuple(zip(*results)) From 44347c9a7d6af4cc284ab897e9a272d240324db4 Mon Sep 17 00:00:00 2001 From: albi3ro Date: Fri, 16 Feb 2024 13:59:21 -0500 Subject: [PATCH 021/428] adding tests from add-simulate branch --- tests/conftest.py | 15 +- tests/lightning_qubit2/test_expval_2.py | 412 +++++++++++++++ tests/lightning_qubit2/test_serialize_2.py | 563 +++++++++++++++++++++ 3 files changed, 985 insertions(+), 5 deletions(-) create mode 100644 tests/lightning_qubit2/test_expval_2.py create mode 100644 tests/lightning_qubit2/test_serialize_2.py diff --git a/tests/conftest.py b/tests/conftest.py index e10a3c203c..25b7ada6e6 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -87,7 +87,7 @@ def n_subsystems(request): # Looking for the device for testing. default_device = "lightning.qubit" -supported_devices = {"lightning.kokkos", "lightning.qubit", "lightning.gpu"} +supported_devices = {"lightning.kokkos", "lightning.qubit", "lightning.qubit2", "lightning.gpu"} supported_devices.update({sb.replace(".", "_") for sb in supported_devices}) @@ -113,10 +113,10 @@ def get_device(): device_name = get_device() -if device_name not in qml.plugin_devices: - raise qml.DeviceError( - f"Device {device_name} does not exist. Make sure the required plugin is installed." - ) +# if device_name not in qml.plugin_devices: +# raise qml.DeviceError( +# f"Device {device_name} does not exist. Make sure the required plugin is installed." +# ) # Device specification import pennylane_lightning.lightning_qubit as lightning_ops # Any definition of lightning_ops will do @@ -131,6 +131,11 @@ def get_device(): if hasattr(pennylane_lightning, "lightning_gpu_ops"): import pennylane_lightning.lightning_gpu_ops as lightning_ops +elif device_name == "lightning.qubit2": + from pennylane_lightning.lightning_qubit import LightningQubit2 as LightningDevice + + if hasattr(pennylane_lightning, "lightning_qubit_ops"): + import pennylane_lightning.lightning_qubit_ops as lightning_ops else: from pennylane_lightning.lightning_qubit import LightningQubit as LightningDevice diff --git a/tests/lightning_qubit2/test_expval_2.py b/tests/lightning_qubit2/test_expval_2.py new file mode 100644 index 0000000000..78553c3e44 --- /dev/null +++ b/tests/lightning_qubit2/test_expval_2.py @@ -0,0 +1,412 @@ +# Copyright 2018-2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +Tests for process and execute (expval calculation). +""" +import pytest + +import numpy as np +import pennylane as qml +from pennylane_lightning.lightning_qubit import LightningQubit2 as LightningQubit +from pennylane.devices import DefaultQubit + +from conftest import LightningDevice # tested device + +if not LightningQubit._CPP_BINARY_AVAILABLE: + pytest.skip("No binary module found. Skipping.", allow_module_level=True) + +if LightningDevice != LightningQubit: + pytest.skip("Exclusive tests for lightning.qubit. Skipping.", allow_module_level=True) + +THETA = np.linspace(0.11, 1, 3) +PHI = np.linspace(0.32, 1, 3) +VARPHI = np.linspace(0.02, 1, 3) + + +@pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI))) +class TestExpval: + """Test expectation value calculations""" + + @pytest.fixture(params=[np.complex64, np.complex128]) + def dev(self, request): + return LightningQubit(c_dtype=request.param) + + @staticmethod + def calculate_reference(tape): + dev = DefaultQubit(max_workers=1) + program, _ = dev.preprocess() + tapes, transf_fn = program([tape]) + results = dev.execute(tapes) + return transf_fn(results) + + @staticmethod + def process_and_execute(dev, tape): + program, _ = dev.preprocess() + tapes, transf_fn = program([tape]) + results = dev.execute(tapes) + return transf_fn(results) + + def test_Identity(self, theta, phi, dev, tol): + """Tests applying identities.""" + + with qml.tape.QuantumTape() as tape: + qml.Identity(wires=[0]) + qml.Identity(wires=[0, 1]) + qml.Identity(wires=[1, 2]) + qml.RX(theta, wires=[0]) + qml.RX(phi, wires=[1]) + qml.expval(qml.PauliX(0)) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_identity_expectation(self, theta, phi, dev, tol): + """Tests identity.""" + + tape = qml.tape.QuantumScript( + [qml.RX(theta, wires=[0]), qml.RX(phi, wires=[1]), qml.CNOT(wires=[0, 1])], + [qml.expval(qml.Identity(wires=[0])), qml.expval(qml.Identity(wires=[1]))], + ) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_multi_wire_identity_expectation(self, theta, phi, dev, tol): + """Tests multi-wire identity.""" + + tape = qml.tape.QuantumScript( + [qml.RX(theta, wires=[0]), qml.RX(phi, wires=[1]), qml.CNOT(wires=[0, 1])], + [qml.expval(qml.Identity(wires=[0, 1]))], + ) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + @pytest.mark.parametrize( + "wires", + [ + ([0, 1]), + (["a", 1]), + (["b", "a"]), + ], + ) + def test_PauliZ_expectation(self, theta, phi, dev, tol, wires): + """Tests PauliZ.""" + + tape = qml.tape.QuantumScript( + [qml.RX(theta, wires=wires[0]), qml.RX(phi, wires=wires[1]), qml.CNOT(wires=wires)], + [qml.expval(qml.PauliZ(wires=wires[0])), qml.expval(qml.PauliZ(wires=wires[1]))], + ) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_PauliX_expectation(self, theta, phi, dev, tol): + """Tests PauliX.""" + + tape = qml.tape.QuantumScript( + [qml.RY(theta, wires=[0]), qml.RY(phi, wires=[1]), qml.CNOT(wires=[0, 1])], + [qml.expval(qml.PauliX(wires=[0])), qml.expval(qml.PauliX(wires=[1]))], + ) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_PauliY_expectation(self, theta, phi, dev, tol): + """Tests PauliY.""" + + tape = qml.tape.QuantumScript( + [qml.RX(theta, wires=[0]), qml.RX(phi, wires=[1]), qml.CNOT(wires=[0, 1])], + [qml.expval(qml.PauliY(wires=[0])), qml.expval(qml.PauliY(wires=[1]))], + ) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_hadamard_expectation(self, theta, phi, dev, tol): + """Tests Hadamard.""" + + tape = qml.tape.QuantumScript( + [qml.RY(theta, wires=[0]), qml.RY(phi, wires=[1]), qml.CNOT(wires=[0, 1])], + [qml.expval(qml.Hadamard(wires=[0])), qml.expval(qml.Hadamard(wires=[1]))], + ) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_hermitian_expectation(self, theta, phi, dev, tol): + """Tests an Hermitian operator.""" + + with qml.tape.QuantumTape() as tape: + qml.RX(theta, wires=0) + qml.RX(phi, wires=1) + qml.RX(theta + phi, wires=2) + + for idx in range(3): + qml.expval(qml.Hermitian([[1, 0], [0, -1]], wires=[idx])) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_hamiltonian_expectation(self, theta, phi, dev, tol): + """Tests a Hamiltonian.""" + + ham = qml.Hamiltonian( + [1.0, 0.3, 0.3, 0.4], + [ + qml.PauliX(0) @ qml.PauliX(1), + qml.PauliZ(0), + qml.PauliZ(1), + qml.PauliX(0) @ qml.PauliY(1), + ], + ) + + with qml.tape.QuantumTape() as tape: + qml.RX(theta, wires=0) + qml.RX(phi, wires=1) + qml.RX(theta + phi, wires=2) + + qml.expval(ham) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_sparse_hamiltonian_expectation(self, theta, phi, dev, tol): + """Tests a Hamiltonian.""" + + ham = qml.SparseHamiltonian( + qml.Hamiltonian( + [1.0, 0.3, 0.3, 0.4], + [ + qml.PauliX(0) @ qml.PauliX(1), + qml.PauliZ(0), + qml.PauliZ(1), + qml.PauliX(0) @ qml.PauliY(1), + ], + ).sparse_matrix(), + wires=[0, 1], + ) + + with qml.tape.QuantumTape() as tape: + qml.RX(theta, wires=0) + qml.RX(phi, wires=1) + + qml.expval(ham) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + +@pytest.mark.parametrize("phi", PHI) +class TestOperatorArithmetic: + """Test integration with SProd, Prod, and Sum.""" + + @pytest.fixture(params=[np.complex64, np.complex128]) + def dev(self, request): + return LightningQubit(c_dtype=request.param) + + @staticmethod + def calculate_reference(tape): + dev = DefaultQubit(max_workers=1) + program, _ = dev.preprocess() + tapes, transf_fn = program([tape]) + results = dev.execute(tapes) + return transf_fn(results) + + @staticmethod + def process_and_execute(dev, tape): + program, _ = dev.preprocess() + tapes, transf_fn = program([tape]) + results = dev.execute(tapes) + return transf_fn(results) + + def test_s_prod(self, phi, dev, tol): + """Tests the `SProd` class.""" + + tape = qml.tape.QuantumScript( + [qml.RX(phi, wires=[0])], + [qml.expval(qml.s_prod(0.5, qml.PauliZ(0)))], + ) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_prod(self, phi, dev, tol): + """Tests the `Prod` class.""" + + tape = qml.tape.QuantumScript( + [qml.RX(phi, wires=[0]), qml.Hadamard(wires=[1]), qml.PauliZ(wires=[1])], + [qml.expval(qml.prod(qml.PauliZ(0), qml.PauliX(1)))], + ) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_sum(self, phi, dev, tol): + """Tests the `Sum` class.""" + tape = qml.tape.QuantumScript( + [qml.RX(phi, wires=[0]), qml.RX(-1.1 * phi, wires=[0])], + [qml.expval(qml.sum(qml.PauliZ(0), qml.PauliX(1)))], + ) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_integration(self, phi, dev, tol): + """Test a Combination of `Sum`, `SProd`, and `Prod`.""" + + obs = qml.sum(qml.s_prod(2.3, qml.PauliZ(0)), -0.5 * qml.prod(qml.PauliY(0), qml.PauliZ(1))) + + tape = qml.tape.QuantumScript( + [qml.RX(phi, wires=[0]), qml.RX(-1.1 * phi, wires=[0])], + [qml.expval(obs)], + ) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + +@pytest.mark.parametrize("theta, phi, varphi", list(zip(THETA, PHI, VARPHI))) +class TestTensorExpval: + """Test tensor expectation values""" + + @pytest.fixture(params=[np.complex64, np.complex128]) + def dev(self, request): + return LightningQubit(c_dtype=request.param) + + @staticmethod + def calculate_reference(tape): + dev = DefaultQubit(max_workers=1) + program, _ = dev.preprocess() + tapes, transf_fn = program([tape]) + results = dev.execute(tapes) + return transf_fn(results) + + @staticmethod + def process_and_execute(dev, tape): + program, _ = dev.preprocess() + tapes, transf_fn = program([tape]) + results = dev.execute(tapes) + return transf_fn(results) + + def test_PauliX_PauliY(self, theta, phi, varphi, dev, tol): + """Tests a tensor product involving PauliX and PauliY.""" + + with qml.tape.QuantumTape() as tape: + qml.RX(theta, wires=[0]) + qml.RX(phi, wires=[1]) + qml.RX(varphi, wires=[2]) + qml.CNOT(wires=[0, 1]) + qml.CNOT(wires=[1, 2]) + qml.expval(qml.PauliX(0) @ qml.PauliY(2)) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_PauliZ_identity(self, theta, phi, varphi, dev, tol): + """Tests a tensor product involving PauliZ and Identity.""" + + with qml.tape.QuantumTape() as tape: + qml.Identity(wires=[0]) + qml.RX(theta, wires=[0]) + qml.RX(phi, wires=[1]) + qml.RX(varphi, wires=[2]) + qml.CNOT(wires=[0, 1]) + qml.CNOT(wires=[1, 2]) + qml.expval(qml.PauliZ(0) @ qml.Identity(1) @ qml.PauliZ(2)) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_PauliZ_hadamard_PauliY(self, theta, phi, varphi, dev, tol): + """Tests a tensor product involving PauliY, PauliZ and Hadamard.""" + + with qml.tape.QuantumTape() as tape: + qml.RX(theta, wires=[0]) + qml.RX(phi, wires=[1]) + qml.RX(varphi, wires=[2]) + qml.CNOT(wires=[0, 1]) + qml.CNOT(wires=[1, 2]) + qml.expval(qml.PauliZ(0) @ qml.Hadamard(1) @ qml.PauliY(2)) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) diff --git a/tests/lightning_qubit2/test_serialize_2.py b/tests/lightning_qubit2/test_serialize_2.py new file mode 100644 index 0000000000..7f14422c38 --- /dev/null +++ b/tests/lightning_qubit2/test_serialize_2.py @@ -0,0 +1,563 @@ +# Copyright 2018-2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +Unit tests for the serialization helper functions. +""" +import pytest +from conftest import device_name, LightningDevice + +import numpy as np +import pennylane as qml +from pennylane_lightning.lightning_qubit._serialize import QuantumScriptSerializer + +# from pennylane_lightning.lightning_qubit._serialize_old import QuantumScriptSerializer + +if not LightningDevice._CPP_BINARY_AVAILABLE: + pytest.skip("No binary module found. Skipping.", allow_module_level=True) + +if device_name == "lightning.kokkos": + from pennylane_lightning.lightning_kokkos_ops.observables import ( + NamedObsC64, + NamedObsC128, + HermitianObsC64, + HermitianObsC128, + TensorProdObsC64, + TensorProdObsC128, + HamiltonianC64, + HamiltonianC128, + SparseHamiltonianC64, + SparseHamiltonianC128, + ) +elif device_name == "lightning.gpu": + from pennylane_lightning.lightning_gpu_ops.observables import ( + NamedObsC64, + NamedObsC128, + HermitianObsC64, + HermitianObsC128, + TensorProdObsC64, + TensorProdObsC128, + HamiltonianC64, + HamiltonianC128, + SparseHamiltonianC64, + SparseHamiltonianC128, + ) +else: + from pennylane_lightning.lightning_qubit_ops.observables import ( + NamedObsC64, + NamedObsC128, + HermitianObsC64, + HermitianObsC128, + TensorProdObsC64, + TensorProdObsC128, + HamiltonianC64, + HamiltonianC128, + SparseHamiltonianC64, + SparseHamiltonianC128, + ) + + +def test_wrong_device_name(): + """Test the device name is not a valid option""" + + with pytest.raises(qml.DeviceError, match="The device name"): + QuantumScriptSerializer("thunder.qubit") + + +@pytest.mark.parametrize( + "obs,obs_type", + [ + (qml.PauliZ(0), NamedObsC128), + (qml.PauliZ(0) @ qml.PauliZ(1), TensorProdObsC128), + (qml.Hadamard(0), NamedObsC128), + (qml.Hermitian(np.eye(2), wires=0), HermitianObsC128), + ( + qml.PauliZ(0) @ qml.Hadamard(1) @ (0.1 * (qml.PauliZ(2) + qml.PauliX(3))), + HamiltonianC128, + ), + ( + ( + qml.Hermitian(np.eye(2), wires=0) + @ qml.Hermitian(np.eye(2), wires=1) + @ qml.Projector([0], wires=2) + ), + TensorProdObsC128, + ), + ( + qml.PauliZ(0) @ qml.Hermitian(np.eye(2), wires=1) @ qml.Projector([0], wires=2), + TensorProdObsC128, + ), + (qml.Projector([0], wires=0), HermitianObsC128), + (qml.Hamiltonian([1], [qml.PauliZ(0)]), HamiltonianC128), + (qml.sum(qml.Hadamard(0), qml.PauliX(1)), HermitianObsC128), + ( + qml.SparseHamiltonian(qml.Hamiltonian([1], [qml.PauliZ(0)]).sparse_matrix(), wires=[0]), + SparseHamiltonianC128, + ), + ], +) +def test_obs_returns_expected_type(obs, obs_type): + """Tests that observables get serialized to the expected type.""" + assert isinstance(QuantumScriptSerializer(device_name)._ob(obs), obs_type) + + +class TestSerializeObs: + """Tests for the serialize_observables function""" + + @pytest.mark.parametrize("use_csingle", [True, False]) + def test_tensor_non_tensor_return(self, use_csingle): + """Test expected serialization for a mixture of tensor product and non-tensor product + return""" + with qml.tape.QuantumTape() as tape: + qml.expval(qml.PauliZ(0) @ qml.PauliX(1)) + qml.expval(qml.Hadamard(1)) + + tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 + named_obs = NamedObsC64 if use_csingle else NamedObsC128 + + s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) + + s_expected = [ + tensor_prod_obs([named_obs("PauliZ", [0]), named_obs("PauliX", [1])]), + named_obs("Hadamard", [1]), + ] + + assert s == s_expected + + @pytest.mark.parametrize("use_csingle", [True, False]) + def test_hermitian_return(self, use_csingle): + """Test expected serialization for a Hermitian return""" + with qml.tape.QuantumTape() as tape: + qml.expval(qml.Hermitian(np.eye(4), wires=[0, 1])) + + hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 + c_dtype = np.complex64 if use_csingle else np.complex128 + + s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) + s_expected = hermitian_obs( + np.array( + [1.0, 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, 1.0], + dtype=c_dtype, + ), + [0, 1], + ) + assert s[0] == s_expected + + @pytest.mark.parametrize("use_csingle", [True, False]) + def test_hermitian_tensor_return(self, use_csingle): + """Test expected serialization for a Hermitian return""" + with qml.tape.QuantumTape() as tape: + qml.expval(qml.Hermitian(np.eye(4), wires=[0, 1]) @ qml.Hermitian(np.eye(2), wires=[2])) + + c_dtype = np.complex64 if use_csingle else np.complex128 + tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 + hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 + s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) + + s_expected = tensor_prod_obs( + [ + hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), + hermitian_obs(np.eye(2, dtype=c_dtype).ravel(), [2]), + ] + ) + + assert s[0] == s_expected + + @pytest.mark.parametrize("use_csingle", [True, False]) + def test_mixed_tensor_return(self, use_csingle): + """Test expected serialization for a mixture of Hermitian and Pauli return""" + with qml.tape.QuantumTape() as tape: + qml.expval(qml.Hermitian(np.eye(4), wires=[0, 1]) @ qml.PauliY(2)) + + c_dtype = np.complex64 if use_csingle else np.complex128 + tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 + hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 + named_obs = NamedObsC64 if use_csingle else NamedObsC128 + + s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) + + s_expected = tensor_prod_obs( + [hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), named_obs("PauliY", [2])] + ) + + assert s[0] == s_expected + + @pytest.mark.parametrize("use_csingle", [True, False]) + def test_hamiltonian_return(self, use_csingle): + """Test expected serialization for a Hamiltonian return""" + + ham = qml.Hamiltonian( + [0.3, 0.5, 0.4], + [ + qml.Hermitian(np.eye(4), wires=[0, 1]) @ qml.PauliY(2), + qml.PauliX(0) @ qml.PauliY(2), + qml.Hermitian(np.ones((8, 8)), wires=range(3)), + ], + ) + + with qml.tape.QuantumTape() as tape: + qml.expval(ham) + + hamiltonian_obs = HamiltonianC64 if use_csingle else HamiltonianC128 + named_obs = NamedObsC64 if use_csingle else NamedObsC128 + hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 + tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 + r_dtype = np.float32 if use_csingle else np.float64 + c_dtype = np.complex64 if use_csingle else np.complex128 + + s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) + + s_expected = hamiltonian_obs( + np.array([0.3, 0.5, 0.4], dtype=r_dtype), + [ + tensor_prod_obs( + [ + hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), + named_obs("PauliY", [2]), + ] + ), + tensor_prod_obs([named_obs("PauliX", [0]), named_obs("PauliY", [2])]), + hermitian_obs(np.ones(64, dtype=c_dtype), [0, 1, 2]), + ], + ) + + assert s[0] == s_expected + + @pytest.mark.parametrize("use_csingle", [True, False]) + def test_hamiltonian_tensor_return(self, use_csingle): + """Test expected serialization for a Hamiltonian return""" + + with qml.tape.QuantumTape() as tape: + ham = qml.Hamiltonian( + [0.3, 0.5, 0.4], + [ + qml.Hermitian(np.eye(4), wires=[0, 1]) @ qml.PauliY(2), + qml.PauliX(0) @ qml.PauliY(2), + qml.Hermitian(np.ones((8, 8)), wires=range(3)), + ], + ) + qml.expval(ham @ qml.PauliZ(3)) + + hamiltonian_obs = HamiltonianC64 if use_csingle else HamiltonianC128 + named_obs = NamedObsC64 if use_csingle else NamedObsC128 + hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 + tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 + r_dtype = np.float32 if use_csingle else np.float64 + c_dtype = np.complex64 if use_csingle else np.complex128 + + s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) + + # Expression (ham @ obs) is converted internally by Pennylane + # where obs is appended to each term of the ham + s_expected = hamiltonian_obs( + np.array([0.3, 0.5, 0.4], dtype=r_dtype), + [ + tensor_prod_obs( + [ + hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), + named_obs("PauliY", [2]), + named_obs("PauliZ", [3]), + ] + ), + tensor_prod_obs( + [named_obs("PauliX", [0]), named_obs("PauliY", [2]), named_obs("PauliZ", [3])] + ), + tensor_prod_obs( + [hermitian_obs(np.ones(64, dtype=c_dtype), [0, 1, 2]), named_obs("PauliZ", [3])] + ), + ], + ) + + assert s[0] == s_expected + + @pytest.mark.parametrize("use_csingle", [True, False]) + def test_hamiltonian_mix_return(self, use_csingle): + """Test expected serialization for a Hamiltonian return""" + + ham1 = qml.Hamiltonian( + [0.3, 0.5, 0.4], + [ + qml.Hermitian(np.eye(4), wires=[0, 1]) @ qml.PauliY(2), + qml.PauliX(0) @ qml.PauliY(2), + qml.Hermitian(np.ones((8, 8)), wires=range(3)), + ], + ) + ham2 = qml.Hamiltonian( + [0.7, 0.3], + [qml.PauliX(0) @ qml.Hermitian(np.eye(4), wires=[1, 2]), qml.PauliY(0) @ qml.PauliX(2)], + ) + + with qml.tape.QuantumTape() as tape: + qml.expval(ham1) + qml.expval(ham2) + + hamiltonian_obs = HamiltonianC64 if use_csingle else HamiltonianC128 + named_obs = NamedObsC64 if use_csingle else NamedObsC128 + hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 + tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 + r_dtype = np.float32 if use_csingle else np.float64 + c_dtype = np.complex64 if use_csingle else np.complex128 + + s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) + + s_expected1 = hamiltonian_obs( + np.array([0.3, 0.5, 0.4], dtype=r_dtype), + [ + tensor_prod_obs( + [ + hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), + named_obs("PauliY", [2]), + ] + ), + tensor_prod_obs([named_obs("PauliX", [0]), named_obs("PauliY", [2])]), + hermitian_obs(np.ones(64, dtype=c_dtype), [0, 1, 2]), + ], + ) + s_expected2 = hamiltonian_obs( + np.array([0.7, 0.3], dtype=r_dtype), + [ + tensor_prod_obs( + [ + named_obs("PauliX", [0]), + hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [1, 2]), + ] + ), + tensor_prod_obs([named_obs("PauliY", [0]), named_obs("PauliX", [2])]), + ], + ) + + assert s[0] == s_expected1 + assert s[1] == s_expected2 + + @pytest.mark.parametrize( + "obs,coeffs,terms", + [ + (qml.prod(qml.PauliZ(0), qml.PauliX(1)), [1], [[("PauliX", 1), ("PauliZ", 0)]]), + (qml.s_prod(0.1, qml.PauliX(0)), [0.1], ("PauliX", 0)), + ( + qml.sum( + 0.5 * qml.prod(qml.PauliX(0), qml.PauliZ(1)), + 0.1 * qml.prod(qml.PauliZ(0), qml.PauliY(1)), + ), + [0.5, 0.1], + [[("PauliZ", 1), ("PauliX", 0)], [("PauliY", 1), ("PauliZ", 0)]], + ), + ], + ) + @pytest.mark.parametrize("use_csingle", [True, False]) + def test_op_arithmetic_uses_hamiltonian(self, use_csingle, obs, coeffs, terms): + """Tests that an arithmetic obs with a PauliRep serializes as a Hamiltonian.""" + tape = qml.tape.QuantumTape(measurements=[qml.expval(obs)]) + res, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) + assert len(res) == 1 + assert isinstance(res[0], HamiltonianC64 if use_csingle else HamiltonianC128) + + hamiltonian_obs = HamiltonianC64 if use_csingle else HamiltonianC128 + tensor_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 + named_obs = NamedObsC64 if use_csingle else NamedObsC128 + rtype = np.float32 if use_csingle else np.float64 + term_shape = np.array(terms).shape + + if len(term_shape) == 1: # just a single pauli op + expected_terms = [named_obs(terms[0], [terms[1]])] + elif len(term_shape) == 3: # list of tensor products + expected_terms = [ + tensor_obs([named_obs(pauli, [wire]) for pauli, wire in term]) for term in terms + ] + + coeffs = np.array(coeffs).astype(rtype) + assert res[0] == hamiltonian_obs(coeffs, expected_terms) + + @pytest.mark.parametrize("use_csingle", [True, False]) + def test_multi_wire_identity(self, use_csingle): + """Tests that multi-wire Identity does not fail serialization.""" + tape = qml.tape.QuantumTape(measurements=[qml.expval(qml.Identity(wires=[1, 2]))]) + res, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) + assert len(res) == 1 + + named_obs = NamedObsC64 if use_csingle else NamedObsC128 + assert res[0] == named_obs("Identity", [1]) + + +class TestSerializeOps: + """Tests for the _ops function""" + + def test_basic_circuit(self): + """Test expected serialization for a simple circuit""" + with qml.tape.QuantumTape() as tape: + qml.RX(0.4, wires=0) + qml.RY(0.6, wires=1) + qml.CNOT(wires=[0, 1]) + + s = QuantumScriptSerializer(device_name).serialize_ops(tape) + s_expected = ( + ( + ["RX", "RY", "CNOT"], + [np.array([0.4]), np.array([0.6]), []], + [[0], [1], [0, 1]], + [False, False, False], + [[], [], []], + [[], [], []], + [[], [], []], + ), + False, + ) + assert s == s_expected + + def test_basic_circuit_not_implemented_ctrl_ops(self): + """Test expected serialization for a simple circuit""" + ops = qml.OrbitalRotation(0.1234, wires=range(4)) + with qml.tape.QuantumTape() as tape: + qml.RX(0.4, wires=0) + qml.RY(0.6, wires=1) + qml.ctrl(ops, [4, 5]) + + s = QuantumScriptSerializer(device_name).serialize_ops(tape) + s_expected = ( + ( + ["RX", "RY", "QubitUnitary"], + [np.array([0.4]), np.array([0.6]), [0.0]], + [[0], [1], list(ops.wires)], + [False, False, False], + [[], [], [qml.matrix(ops)]], + [[], [], [4, 5]], + ), + False, + ) + assert s[0][0] == s_expected[0][0] + assert s[0][1] == s_expected[0][1] + assert s[0][2] == s_expected[0][2] + assert s[0][3] == s_expected[0][3] + assert all(np.allclose(s0, s1) for s0, s1 in zip(s[0][4], s_expected[0][4])) + assert s[0][5] == s_expected[0][5] + assert s[1] == s_expected[1] + + def test_multicontrolledx(self): + """Test expected serialization for a simple circuit""" + with qml.tape.QuantumTape() as tape: + qml.RX(0.4, wires=0) + qml.RY(0.6, wires=1) + qml.ctrl(qml.PauliX(wires=0), [1, 2, 3], control_values=[True, False, False]) + + s = QuantumScriptSerializer(device_name).serialize_ops(tape) + s_expected = ( + ( + ["RX", "RY", "PauliX"], + [np.array([0.4]), np.array([0.6]), []], + [[0], [1], [0]], + [False, False, False], + [[], [], []], + [[], [], [1, 2, 3]], + [[], [], [True, False, False]], + ), + False, + ) + assert s == s_expected + + @pytest.mark.parametrize("stateprep", [qml.QubitStateVector, qml.StatePrep]) + def test_skips_prep_circuit(self, stateprep): + """Test expected serialization for a simple circuit with state preparation, such that + the state preparation is skipped""" + with qml.tape.QuantumTape() as tape: + stateprep([1, 0], wires=0) + qml.BasisState([1], wires=1) + qml.RX(0.4, wires=0) + qml.RY(0.6, wires=1) + qml.CNOT(wires=[0, 1]) + + s = QuantumScriptSerializer(device_name).serialize_ops(tape) + s_expected = ( + ( + ["RX", "RY", "CNOT"], + [[0.4], [0.6], []], + [[0], [1], [0, 1]], + [False, False, False], + [[], [], []], + [[], [], []], + [[], [], []], + ), + True, + ) + assert s == s_expected + + def test_unsupported_kernel_circuit(self): + """Test expected serialization for a circuit including gates that do not have a dedicated + kernel""" + with qml.tape.QuantumTape() as tape: + qml.CNOT(wires=[0, 1]) + qml.RZ(0.2, wires=2) + + s = QuantumScriptSerializer(device_name).serialize_ops(tape) + s_expected = ( + ( + ["CNOT", "RZ"], + [[], [0.2]], + [[0, 1], [2]], + [False, False], + ), + False, + ) + assert s[0][0] == s_expected[0][0] + assert s[0][1] == s_expected[0][1] + + @pytest.mark.parametrize("C", [True, False]) + def test_integration(self, C): + """Test expected serialization for a random circuit""" + with qml.tape.QuantumTape() as tape: + qml.RX(0.4, wires=0) + qml.RY(0.6, wires=1) + qml.CNOT(wires=[0, 1]) + qml.QubitUnitary(np.eye(4), wires=[0, 1]) + qml.templates.QFT(wires=[0, 1, 2]) + qml.DoubleExcitation(0.555, wires=[3, 2, 1, 0]) + qml.DoubleExcitationMinus(0.555, wires=[0, 1, 2, 3]) + qml.DoubleExcitationPlus(0.555, wires=[0, 1, 2, 3]) + + s = QuantumScriptSerializer(device_name).serialize_ops(tape) + + dtype = np.complex64 if C else np.complex128 + s_expected = ( + ( + [ + "RX", + "RY", + "CNOT", + "QubitUnitary", + "QFT", + "DoubleExcitation", + "DoubleExcitationMinus", + "DoubleExcitationPlus", + ], + [[0.4], [0.6], [], [0.0], [], [0.555], [0.555], [0.555]], + [[0], [1], [0, 1], [0, 1], [0, 1, 2], [3, 2, 1, 0], [0, 1, 2, 3], [0, 1, 2, 3]], + [False, False, False, False, False, False, False, False], + [ + [], + [], + [], + qml.matrix(qml.QubitUnitary(np.eye(4, dtype=dtype), wires=[0, 1])), + qml.matrix(qml.templates.QFT(wires=[0, 1, 2])), + [], + [], + [], + ], + ), + False, + ) + assert s[0][0] == s_expected[0][0] + assert s[0][1] == s_expected[0][1] + assert s[0][2] == s_expected[0][2] + assert s[0][3] == s_expected[0][3] + assert s[1] == s_expected[1] + + assert all(np.allclose(s1, s2) for s1, s2 in zip(s[0][4], s_expected[0][4])) From d784b5f2d9146bc18dce87d4d471cc03c3712682 Mon Sep 17 00:00:00 2001 From: albi3ro Date: Fri, 16 Feb 2024 14:01:53 -0500 Subject: [PATCH 022/428] remove python class to reverse order of PRs --- .../lightning_qubit/lightning_qubit2.py | 349 +-------------- tests/lightning_qubit2/test_expval_2.py | 412 ------------------ 2 files changed, 3 insertions(+), 758 deletions(-) delete mode 100644 tests/lightning_qubit2/test_expval_2.py diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index de79f1d143..e169b5d077 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -20,19 +20,9 @@ import numpy as np import pennylane as qml -from pennylane.devices import Device, ExecutionConfig, DefaultExecutionConfig -from pennylane.devices.preprocess import ( - decompose, - validate_device_wires, - decompose, - validate_measurements, - validate_observables, - no_sampling, -) -from pennylane.devices.qubit.sampling import get_num_shots_and_executions + + from pennylane.tape import QuantumScript -from pennylane.transforms.core import TransformProgram -from pennylane.typing import Result, ResultBatch from ._state_vector import LightningStateVector from ._measurements import LightningMeasurements @@ -73,337 +63,4 @@ def dummy_jacobian(circuit: QuantumScript): def simulate_and_jacobian(circuit: QuantumScript): - return np.array(0.0), np.array(0.0) - - -Result_or_ResultBatch = Union[Result, ResultBatch] -QuantumTapeBatch = Sequence[qml.tape.QuantumTape] -QuantumTape_or_Batch = Union[qml.tape.QuantumTape, QuantumTapeBatch] - - -_operations = frozenset( - { - "Identity", - "BasisState", - "QubitStateVector", - "StatePrep", - "QubitUnitary", - "ControlledQubitUnitary", - "MultiControlledX", - "DiagonalQubitUnitary", - "PauliX", - "PauliY", - "PauliZ", - "MultiRZ", - "Hadamard", - "S", - "Adjoint(S)", - "T", - "Adjoint(T)", - "SX", - "Adjoint(SX)", - "CNOT", - "SWAP", - "ISWAP", - "PSWAP", - "Adjoint(ISWAP)", - "SISWAP", - "Adjoint(SISWAP)", - "SQISW", - "CSWAP", - "Toffoli", - "CY", - "CZ", - "PhaseShift", - "ControlledPhaseShift", - "CPhase", - "RX", - "RY", - "RZ", - "Rot", - "CRX", - "CRY", - "CRZ", - "C(PauliX)", - "C(PauliY)", - "C(PauliZ)", - "C(Hadamard)", - "C(S)", - "C(T)", - "C(PhaseShift)", - "C(RX)", - "C(RY)", - "C(RZ)", - "C(SWAP)", - "C(IsingXX)", - "C(IsingXY)", - "C(IsingYY)", - "C(IsingZZ)", - "C(SingleExcitation)", - "C(SingleExcitationMinus)", - "C(SingleExcitationPlus)", - "C(DoubleExcitation)", - "C(DoubleExcitationMinus)", - "C(DoubleExcitationPlus)", - "CRot", - "IsingXX", - "IsingYY", - "IsingZZ", - "IsingXY", - "SingleExcitation", - "SingleExcitationPlus", - "SingleExcitationMinus", - "DoubleExcitation", - "DoubleExcitationPlus", - "DoubleExcitationMinus", - "QubitCarry", - "QubitSum", - "OrbitalRotation", - "QFT", - "ECR", - "BlockEncode", - } -) - -_observables = frozenset( - { - "PauliX", - "PauliY", - "PauliZ", - "Hadamard", - "Hermitian", - "Identity", - "Projector", - "SparseHamiltonian", - "Hamiltonian", - "Sum", - "SProd", - "Prod", - "Exp", - } -) - - -def stopping_condition(op: qml.operation.Operator) -> bool: - return op.name in _operations - - -def accepted_observables(obs: qml.operation.Operator) -> bool: - return obs.name in _observables - - -def accepted_measurements(m: qml.measurements.MeasurementProcess) -> bool: - return isinstance(m, (qml.measurements.ExpectationMP)) - - -class LightningQubit2(Device): - """PennyLane Lightning Qubit device. - - A device that interfaces with C++ to perform fast linear algebra calculations. - - Use of this device requires pre-built binaries or compilation from source. Check out the - :doc:`/lightning_qubit/installation` guide for more details. - - Args: - wires (int): the number of wires to initialize the device with - c_dtype: Datatypes for statevector representation. Must be one of - ``np.complex64`` or ``np.complex128``. - shots (int): How many times the circuit should be evaluated (or sampled) to estimate - the expectation values. Defaults to ``None`` if not specified. Setting - to ``None`` results in computing statistics like expectation values and - variances analytically. - seed (str, int, rng) - mcmc (bool): Determine whether to use the approximate Markov Chain Monte Carlo - sampling method when generating samples. - kernel_name (str): name of transition kernel. The current version supports - two kernels: ``"Local"`` and ``"NonZeroRandom"``. - The local kernel conducts a bit-flip local transition between states. - The local kernel generates a random qubit site and then generates a random - number to determine the new bit at that qubit site. The ``"NonZeroRandom"`` kernel - randomly transits between states that have nonzero probability. - num_burnin (int): number of steps that will be dropped. Increasing this value will - result in a closer approximation but increased runtime. - batch_obs (bool): Determine whether we process observables in parallel when - computing the jacobian. This value is only relevant when the lightning - qubit is built with OpenMP. - """ - - name = "lightning.qubit2" - _CPP_BINARY_AVAILABLE = LQ_CPP_BINARY_AVAILABLE - - _device_options = ["rng", "c_dtype", "batch_obs", "mcmc", "kernel_name", "num_burnin"] - - def __init__( # pylint: disable=too-many-arguments - self, - wires=None, - *, - c_dtype=np.complex128, - shots=None, - seed="global", - mcmc=False, - kernel_name="Local", - num_burnin=100, - batch_obs=False, - ): - super().__init__(wires=wires, shots=shots) - seed = np.random.randint(0, high=10000000) if seed == "global" else seed - self._rng = np.random.default_rng(seed) - - self._c_dtype = c_dtype - self._batch_obs = batch_obs - self._mcmc = mcmc - if self._mcmc: - if kernel_name not in [ - "Local", - "NonZeroRandom", - ]: - raise NotImplementedError( - f"The {kernel_name} is not supported and currently " - "only 'Local' and 'NonZeroRandom' kernels are supported." - ) - if num_burnin >= shots: - raise ValueError("Shots should be greater than num_burnin.") - self._kernel_name = kernel_name - self._num_burnin = num_burnin - else: - self._kernel_name = None - self._num_burnin = None - - @property - def c_dtype(self): - """State vector complex data type.""" - return self._c_dtype - - @property - def operation(self) -> frozenset[str]: - """The names of supported operations.""" - return _operations - - @property - def observables(self) -> frozenset[str]: - """The names of supported observables.""" - return _observables - - def _setup_execution_config(self, config): - updated_values = {} - if config.gradient_method == "best": - updated_values["gradient_method"] = "adjoint" - if config.use_device_gradient is None: - updated_values["use_device_gradient"] = config.gradient_method in ("best", "adjoint") - if config.grad_on_execution is None: - updated_values["grad_on_execution"] = True - - new_device_options = dict(config.device_options) - for option in self._device_options: - if option not in new_device_options: - new_device_options[option] = getattr(self, f"_{option}", None) - - return replace(config, **updated_values, device_options=new_device_options) - - def supports_derivatives( - self, - execution_config: Optional[ExecutionConfig] = None, - circuit: Optional[qml.tape.QuantumTape] = None, - ) -> bool: - if execution_config is None and circuit is None: - return True - if execution_config.gradient_method not in {"adjoint", "best"}: - return False - if circuit is None: - return True - return ( - all(isinstance(m, qml.measurements.ExpectationMP) for m in circuit.measurements) - and not circuit.shots - ) - - def preprocess(self, execution_config: ExecutionConfig = DefaultExecutionConfig): - program = TransformProgram() - program.add_transform( - validate_measurements, analytic_measurements=accepted_measurements, name=self.name - ) - program.add_transform(no_sampling) - program.add_transform(validate_observables, accepted_observables, name=self.name) - program.add_transform(validate_device_wires, self.wires, name=self.name) - program.add_transform(qml.defer_measurements, device=self) - program.add_transform(decompose, stopping_condition=stopping_condition, name=self.name) - program.add_transform(qml.transforms.broadcast_expand) - return program, self._setup_execution_config(execution_config) - - def _execute_tracking(self, circuits): - self.tracker.update(batches=1) - self.tracker.record() - for c in circuits: - qpu_executions, shots = get_num_shots_and_executions(c) - if c.shots: - self.tracker.update( - simulations=1, - executions=qpu_executions, - shots=shots, - ) - else: - self.tracker.update( - simulations=1, - executions=qpu_executions, - ) - self.tracker.record() - - def execute( - self, - circuits: QuantumTape_or_Batch, - execution_config: ExecutionConfig = DefaultExecutionConfig, - ) -> Result_or_ResultBatch: - is_single_circuit = False - if isinstance(circuits, QuantumScript): - is_single_circuit = True - circuits = (circuits,) - - if self.tracker.active: - self._execute_tracking(circuits) - - results = [] - for circuit in circuits: - circuit = circuit.map_to_standard_wires() - results.append(simulate(circuit, **execution_config.device_options)) - - return results[0] if is_single_circuit else tuple(results) - - def compute_derivatives( - self, - circuits: QuantumTape_or_Batch, - execution_config: ExecutionConfig = DefaultExecutionConfig, - ): - is_single_circuit = False - if isinstance(circuits, QuantumScript): - is_single_circuit = True - circuits = [circuits] - - if self.tracker.active: - self.tracker.update(derivative_batches=1, derivatives=len(circuits)) - self.tracker.record() - res = tuple(dummy_jacobian(circuit) for circuit in circuits) - - return res[0] if is_single_circuit else res - - def execute_and_compute_derivatives( - self, - circuits: QuantumTape_or_Batch, - execution_config: ExecutionConfig = DefaultExecutionConfig, - ): - is_single_circuit = False - if isinstance(circuits, QuantumScript): - is_single_circuit = True - circuits = [circuits] - - if self.tracker.active: - for c in circuits: - self.tracker.update(resources=c.specs["resources"]) - self.tracker.update( - execute_and_derivative_batches=1, - executions=len(circuits), - derivatives=len(circuits), - ) - self.tracker.record() - - results = tuple(simulate_and_jacobian(c) for c in circuits) - results, jacs = tuple(zip(*results)) - return (results[0], jacs[0]) if is_single_circuit else (results, jacs) + return np.array(0.0), np.array(0.0) \ No newline at end of file diff --git a/tests/lightning_qubit2/test_expval_2.py b/tests/lightning_qubit2/test_expval_2.py deleted file mode 100644 index 78553c3e44..0000000000 --- a/tests/lightning_qubit2/test_expval_2.py +++ /dev/null @@ -1,412 +0,0 @@ -# Copyright 2018-2024 Xanadu Quantum Technologies Inc. - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -""" -Tests for process and execute (expval calculation). -""" -import pytest - -import numpy as np -import pennylane as qml -from pennylane_lightning.lightning_qubit import LightningQubit2 as LightningQubit -from pennylane.devices import DefaultQubit - -from conftest import LightningDevice # tested device - -if not LightningQubit._CPP_BINARY_AVAILABLE: - pytest.skip("No binary module found. Skipping.", allow_module_level=True) - -if LightningDevice != LightningQubit: - pytest.skip("Exclusive tests for lightning.qubit. Skipping.", allow_module_level=True) - -THETA = np.linspace(0.11, 1, 3) -PHI = np.linspace(0.32, 1, 3) -VARPHI = np.linspace(0.02, 1, 3) - - -@pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI))) -class TestExpval: - """Test expectation value calculations""" - - @pytest.fixture(params=[np.complex64, np.complex128]) - def dev(self, request): - return LightningQubit(c_dtype=request.param) - - @staticmethod - def calculate_reference(tape): - dev = DefaultQubit(max_workers=1) - program, _ = dev.preprocess() - tapes, transf_fn = program([tape]) - results = dev.execute(tapes) - return transf_fn(results) - - @staticmethod - def process_and_execute(dev, tape): - program, _ = dev.preprocess() - tapes, transf_fn = program([tape]) - results = dev.execute(tapes) - return transf_fn(results) - - def test_Identity(self, theta, phi, dev, tol): - """Tests applying identities.""" - - with qml.tape.QuantumTape() as tape: - qml.Identity(wires=[0]) - qml.Identity(wires=[0, 1]) - qml.Identity(wires=[1, 2]) - qml.RX(theta, wires=[0]) - qml.RX(phi, wires=[1]) - qml.expval(qml.PauliX(0)) - - calculated_val = self.process_and_execute(dev, tape) - reference_val = self.calculate_reference(tape) - - tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 - - assert np.allclose(calculated_val, reference_val, tol) - - def test_identity_expectation(self, theta, phi, dev, tol): - """Tests identity.""" - - tape = qml.tape.QuantumScript( - [qml.RX(theta, wires=[0]), qml.RX(phi, wires=[1]), qml.CNOT(wires=[0, 1])], - [qml.expval(qml.Identity(wires=[0])), qml.expval(qml.Identity(wires=[1]))], - ) - - calculated_val = self.process_and_execute(dev, tape) - reference_val = self.calculate_reference(tape) - - tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 - - assert np.allclose(calculated_val, reference_val, tol) - - def test_multi_wire_identity_expectation(self, theta, phi, dev, tol): - """Tests multi-wire identity.""" - - tape = qml.tape.QuantumScript( - [qml.RX(theta, wires=[0]), qml.RX(phi, wires=[1]), qml.CNOT(wires=[0, 1])], - [qml.expval(qml.Identity(wires=[0, 1]))], - ) - - calculated_val = self.process_and_execute(dev, tape) - reference_val = self.calculate_reference(tape) - - tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 - - assert np.allclose(calculated_val, reference_val, tol) - - @pytest.mark.parametrize( - "wires", - [ - ([0, 1]), - (["a", 1]), - (["b", "a"]), - ], - ) - def test_PauliZ_expectation(self, theta, phi, dev, tol, wires): - """Tests PauliZ.""" - - tape = qml.tape.QuantumScript( - [qml.RX(theta, wires=wires[0]), qml.RX(phi, wires=wires[1]), qml.CNOT(wires=wires)], - [qml.expval(qml.PauliZ(wires=wires[0])), qml.expval(qml.PauliZ(wires=wires[1]))], - ) - - calculated_val = self.process_and_execute(dev, tape) - reference_val = self.calculate_reference(tape) - - tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 - - assert np.allclose(calculated_val, reference_val, tol) - - def test_PauliX_expectation(self, theta, phi, dev, tol): - """Tests PauliX.""" - - tape = qml.tape.QuantumScript( - [qml.RY(theta, wires=[0]), qml.RY(phi, wires=[1]), qml.CNOT(wires=[0, 1])], - [qml.expval(qml.PauliX(wires=[0])), qml.expval(qml.PauliX(wires=[1]))], - ) - - calculated_val = self.process_and_execute(dev, tape) - reference_val = self.calculate_reference(tape) - - tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 - - assert np.allclose(calculated_val, reference_val, tol) - - def test_PauliY_expectation(self, theta, phi, dev, tol): - """Tests PauliY.""" - - tape = qml.tape.QuantumScript( - [qml.RX(theta, wires=[0]), qml.RX(phi, wires=[1]), qml.CNOT(wires=[0, 1])], - [qml.expval(qml.PauliY(wires=[0])), qml.expval(qml.PauliY(wires=[1]))], - ) - - calculated_val = self.process_and_execute(dev, tape) - reference_val = self.calculate_reference(tape) - - tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 - - assert np.allclose(calculated_val, reference_val, tol) - - def test_hadamard_expectation(self, theta, phi, dev, tol): - """Tests Hadamard.""" - - tape = qml.tape.QuantumScript( - [qml.RY(theta, wires=[0]), qml.RY(phi, wires=[1]), qml.CNOT(wires=[0, 1])], - [qml.expval(qml.Hadamard(wires=[0])), qml.expval(qml.Hadamard(wires=[1]))], - ) - - calculated_val = self.process_and_execute(dev, tape) - reference_val = self.calculate_reference(tape) - - tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 - - assert np.allclose(calculated_val, reference_val, tol) - - def test_hermitian_expectation(self, theta, phi, dev, tol): - """Tests an Hermitian operator.""" - - with qml.tape.QuantumTape() as tape: - qml.RX(theta, wires=0) - qml.RX(phi, wires=1) - qml.RX(theta + phi, wires=2) - - for idx in range(3): - qml.expval(qml.Hermitian([[1, 0], [0, -1]], wires=[idx])) - - calculated_val = self.process_and_execute(dev, tape) - reference_val = self.calculate_reference(tape) - - tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 - - assert np.allclose(calculated_val, reference_val, tol) - - def test_hamiltonian_expectation(self, theta, phi, dev, tol): - """Tests a Hamiltonian.""" - - ham = qml.Hamiltonian( - [1.0, 0.3, 0.3, 0.4], - [ - qml.PauliX(0) @ qml.PauliX(1), - qml.PauliZ(0), - qml.PauliZ(1), - qml.PauliX(0) @ qml.PauliY(1), - ], - ) - - with qml.tape.QuantumTape() as tape: - qml.RX(theta, wires=0) - qml.RX(phi, wires=1) - qml.RX(theta + phi, wires=2) - - qml.expval(ham) - - calculated_val = self.process_and_execute(dev, tape) - reference_val = self.calculate_reference(tape) - - tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 - - assert np.allclose(calculated_val, reference_val, tol) - - def test_sparse_hamiltonian_expectation(self, theta, phi, dev, tol): - """Tests a Hamiltonian.""" - - ham = qml.SparseHamiltonian( - qml.Hamiltonian( - [1.0, 0.3, 0.3, 0.4], - [ - qml.PauliX(0) @ qml.PauliX(1), - qml.PauliZ(0), - qml.PauliZ(1), - qml.PauliX(0) @ qml.PauliY(1), - ], - ).sparse_matrix(), - wires=[0, 1], - ) - - with qml.tape.QuantumTape() as tape: - qml.RX(theta, wires=0) - qml.RX(phi, wires=1) - - qml.expval(ham) - - calculated_val = self.process_and_execute(dev, tape) - reference_val = self.calculate_reference(tape) - - tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 - - assert np.allclose(calculated_val, reference_val, tol) - - -@pytest.mark.parametrize("phi", PHI) -class TestOperatorArithmetic: - """Test integration with SProd, Prod, and Sum.""" - - @pytest.fixture(params=[np.complex64, np.complex128]) - def dev(self, request): - return LightningQubit(c_dtype=request.param) - - @staticmethod - def calculate_reference(tape): - dev = DefaultQubit(max_workers=1) - program, _ = dev.preprocess() - tapes, transf_fn = program([tape]) - results = dev.execute(tapes) - return transf_fn(results) - - @staticmethod - def process_and_execute(dev, tape): - program, _ = dev.preprocess() - tapes, transf_fn = program([tape]) - results = dev.execute(tapes) - return transf_fn(results) - - def test_s_prod(self, phi, dev, tol): - """Tests the `SProd` class.""" - - tape = qml.tape.QuantumScript( - [qml.RX(phi, wires=[0])], - [qml.expval(qml.s_prod(0.5, qml.PauliZ(0)))], - ) - - calculated_val = self.process_and_execute(dev, tape) - reference_val = self.calculate_reference(tape) - - tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 - - assert np.allclose(calculated_val, reference_val, tol) - - def test_prod(self, phi, dev, tol): - """Tests the `Prod` class.""" - - tape = qml.tape.QuantumScript( - [qml.RX(phi, wires=[0]), qml.Hadamard(wires=[1]), qml.PauliZ(wires=[1])], - [qml.expval(qml.prod(qml.PauliZ(0), qml.PauliX(1)))], - ) - - calculated_val = self.process_and_execute(dev, tape) - reference_val = self.calculate_reference(tape) - - tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 - - assert np.allclose(calculated_val, reference_val, tol) - - def test_sum(self, phi, dev, tol): - """Tests the `Sum` class.""" - tape = qml.tape.QuantumScript( - [qml.RX(phi, wires=[0]), qml.RX(-1.1 * phi, wires=[0])], - [qml.expval(qml.sum(qml.PauliZ(0), qml.PauliX(1)))], - ) - - calculated_val = self.process_and_execute(dev, tape) - reference_val = self.calculate_reference(tape) - - tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 - - assert np.allclose(calculated_val, reference_val, tol) - - def test_integration(self, phi, dev, tol): - """Test a Combination of `Sum`, `SProd`, and `Prod`.""" - - obs = qml.sum(qml.s_prod(2.3, qml.PauliZ(0)), -0.5 * qml.prod(qml.PauliY(0), qml.PauliZ(1))) - - tape = qml.tape.QuantumScript( - [qml.RX(phi, wires=[0]), qml.RX(-1.1 * phi, wires=[0])], - [qml.expval(obs)], - ) - - calculated_val = self.process_and_execute(dev, tape) - reference_val = self.calculate_reference(tape) - - tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 - - assert np.allclose(calculated_val, reference_val, tol) - - -@pytest.mark.parametrize("theta, phi, varphi", list(zip(THETA, PHI, VARPHI))) -class TestTensorExpval: - """Test tensor expectation values""" - - @pytest.fixture(params=[np.complex64, np.complex128]) - def dev(self, request): - return LightningQubit(c_dtype=request.param) - - @staticmethod - def calculate_reference(tape): - dev = DefaultQubit(max_workers=1) - program, _ = dev.preprocess() - tapes, transf_fn = program([tape]) - results = dev.execute(tapes) - return transf_fn(results) - - @staticmethod - def process_and_execute(dev, tape): - program, _ = dev.preprocess() - tapes, transf_fn = program([tape]) - results = dev.execute(tapes) - return transf_fn(results) - - def test_PauliX_PauliY(self, theta, phi, varphi, dev, tol): - """Tests a tensor product involving PauliX and PauliY.""" - - with qml.tape.QuantumTape() as tape: - qml.RX(theta, wires=[0]) - qml.RX(phi, wires=[1]) - qml.RX(varphi, wires=[2]) - qml.CNOT(wires=[0, 1]) - qml.CNOT(wires=[1, 2]) - qml.expval(qml.PauliX(0) @ qml.PauliY(2)) - - calculated_val = self.process_and_execute(dev, tape) - reference_val = self.calculate_reference(tape) - - tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 - - assert np.allclose(calculated_val, reference_val, tol) - - def test_PauliZ_identity(self, theta, phi, varphi, dev, tol): - """Tests a tensor product involving PauliZ and Identity.""" - - with qml.tape.QuantumTape() as tape: - qml.Identity(wires=[0]) - qml.RX(theta, wires=[0]) - qml.RX(phi, wires=[1]) - qml.RX(varphi, wires=[2]) - qml.CNOT(wires=[0, 1]) - qml.CNOT(wires=[1, 2]) - qml.expval(qml.PauliZ(0) @ qml.Identity(1) @ qml.PauliZ(2)) - - calculated_val = self.process_and_execute(dev, tape) - reference_val = self.calculate_reference(tape) - - tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 - - assert np.allclose(calculated_val, reference_val, tol) - - def test_PauliZ_hadamard_PauliY(self, theta, phi, varphi, dev, tol): - """Tests a tensor product involving PauliY, PauliZ and Hadamard.""" - - with qml.tape.QuantumTape() as tape: - qml.RX(theta, wires=[0]) - qml.RX(phi, wires=[1]) - qml.RX(varphi, wires=[2]) - qml.CNOT(wires=[0, 1]) - qml.CNOT(wires=[1, 2]) - qml.expval(qml.PauliZ(0) @ qml.Hadamard(1) @ qml.PauliY(2)) - - calculated_val = self.process_and_execute(dev, tape) - reference_val = self.calculate_reference(tape) - - tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 - - assert np.allclose(calculated_val, reference_val, tol) From 2a8cd8daa4ec56f1eab9717f90beecb01ecd8828 Mon Sep 17 00:00:00 2001 From: albi3ro Date: Fri, 16 Feb 2024 15:08:00 -0500 Subject: [PATCH 023/428] merge conflicts --- .../lightning_qubit/lightning_qubit2.py | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index b0b6272ec8..fe6824e4a3 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -15,23 +15,10 @@ This module contains the LightningQubit2 class that inherits from the new device interface. """ -from typing import Union, Sequence, Optional +from typing import Optional, Union, Sequence, Callable from dataclasses import replace import numpy as np - -try: - # pylint: disable=import-error, no-name-in-module - from pennylane_lightning.lightning_qubit_ops import ( - StateVectorC64, - StateVectorC128, - ) - - LQ_CPP_BINARY_AVAILABLE = True - except ImportError: - LQ_CPP_BINARY_AVAILABLE = False - - import pennylane as qml from pennylane.devices import Device, ExecutionConfig, DefaultExecutionConfig from pennylane.devices.modifiers import convert_single_circuit_to_batch, simulator_tracking @@ -43,8 +30,7 @@ validate_observables, no_sampling, ) -from pennylane.devices.qubit.sampling import get_num_shots_and_executions -from pennylane.tape import QuantumScript +from pennylane.tape import QuantumTape from pennylane.transforms.core import TransformProgram from pennylane.typing import Result, ResultBatch from pennylane_lightning.lightning_qubit.device_modifiers import convert_single_circuit_to_batch @@ -52,9 +38,25 @@ from ._state_vector import LightningStateVector from ._measurements import LightningMeasurements +try: + # pylint: disable=import-error, no-name-in-module + from pennylane_lightning.lightning_qubit_ops import ( + StateVectorC64, + StateVectorC128, + ) + + LQ_CPP_BINARY_AVAILABLE = True +except ImportError: + LQ_CPP_BINARY_AVAILABLE = False -def simulate(circuit: QuantumScript, dtype=np.complex128) -> Result: - """Simulate a single quantum script. +Result_or_ResultBatch = Union[Result, ResultBatch] +QuantumTapeBatch = Sequence[QuantumTape] +QuantumTape_or_Batch = Union[QuantumTape, QuantumTapeBatch] +PostprocessingFn = Callable[[ResultBatch], Result_or_ResultBatch] + + +def simulate(circuit: QuantumTape, dtype=np.complex128) -> Result: + """Simulate a single quantum script.a Args: circuit (QuantumTape): The single circuit to simulate @@ -71,16 +73,14 @@ def simulate(circuit: QuantumScript, dtype=np.complex128) -> Result: return LightningMeasurements(state).measure_final_state(circuit) -def dummy_jacobian(circuit: QuantumScript): +def dummy_jacobian(circuit: QuantumTape): return np.array(0.0) -def simulate_and_jacobian(circuit: QuantumScript): +def simulate_and_jacobian(circuit: QuantumTape): return np.array(0.0), np.array(0.0) -from pennylane.tape import QuantumScript - _operations = frozenset( { "Identity", From 87779ea85337cd108f5a6e82d1127d635f9e0d02 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Fri, 16 Feb 2024 15:17:03 -0500 Subject: [PATCH 024/428] update simulate to get a LightningStateVector --- pennylane_lightning/lightning_qubit/lightning_qubit2.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index e169b5d077..0b2e555121 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -40,7 +40,7 @@ LQ_CPP_BINARY_AVAILABLE = False -def simulate(circuit: QuantumScript, dtype=np.complex128) -> Result: +def simulate(circuit: QuantumScript, state: LightningStateVector, dtype=np.complex128) -> Result: """Simulate a single quantum script. Args: @@ -54,8 +54,8 @@ def simulate(circuit: QuantumScript, dtype=np.complex128) -> Result: Note that this function can return measurements for non-commuting observables simultaneously. """ - state = LightningStateVector(num_wires=circuit.num_wires, dtype=dtype).get_final_state(circuit) - return LightningMeasurements(state).measure_final_state(circuit) + final_state = state.get_final_state(circuit) + return LightningMeasurements(final_state).measure_final_state(circuit) def dummy_jacobian(circuit: QuantumScript): From 9bbff300e8561de68b25d0f96ca4702aee6f6042 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Fri, 16 Feb 2024 15:23:38 -0500 Subject: [PATCH 025/428] add reset state --- pennylane_lightning/lightning_qubit/lightning_qubit2.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index 0b2e555121..27a2bd7b74 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -54,6 +54,7 @@ def simulate(circuit: QuantumScript, state: LightningStateVector, dtype=np.compl Note that this function can return measurements for non-commuting observables simultaneously. """ + state = state.reset_state() final_state = state.get_final_state(circuit) return LightningMeasurements(final_state).measure_final_state(circuit) @@ -63,4 +64,4 @@ def dummy_jacobian(circuit: QuantumScript): def simulate_and_jacobian(circuit: QuantumScript): - return np.array(0.0), np.array(0.0) \ No newline at end of file + return np.array(0.0), np.array(0.0) From b4ed92b718df52db00f5f38cc6c5cb10b243876e Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Fri, 16 Feb 2024 15:26:05 -0500 Subject: [PATCH 026/428] update simulate --- pennylane_lightning/lightning_qubit/lightning_qubit2.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index 27a2bd7b74..63d7bedc9f 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -54,8 +54,8 @@ def simulate(circuit: QuantumScript, state: LightningStateVector, dtype=np.compl Note that this function can return measurements for non-commuting observables simultaneously. """ - state = state.reset_state() - final_state = state.get_final_state(circuit) + final_state = state.reset_state() + final_state = final_state.get_final_state(circuit) return LightningMeasurements(final_state).measure_final_state(circuit) From 16cd1f82034ce5b507502c107b931ce80b0fa68b Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Fri, 16 Feb 2024 15:27:31 -0500 Subject: [PATCH 027/428] update docs --- pennylane_lightning/lightning_qubit/lightning_qubit2.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index 63d7bedc9f..9f46a69de3 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -45,6 +45,7 @@ def simulate(circuit: QuantumScript, state: LightningStateVector, dtype=np.compl Args: circuit (QuantumTape): The single circuit to simulate + state (LightningStateVector): handle to Lightning state vector dtype: Datatypes for state-vector representation. Must be one of ``np.complex64`` or ``np.complex128``. From cd2828a6a562ebea308f9036d60d307d797e9c79 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Fri, 16 Feb 2024 15:31:26 -0500 Subject: [PATCH 028/428] add Result import --- pennylane_lightning/lightning_qubit/lightning_qubit2.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index 9f46a69de3..08a3a28251 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -23,6 +23,7 @@ from pennylane.tape import QuantumScript +from pennylane.typing import Result from ._state_vector import LightningStateVector from ._measurements import LightningMeasurements From fde6172023a8a0d931b4aac1b3b4afafc4c53427 Mon Sep 17 00:00:00 2001 From: albi3ro Date: Fri, 16 Feb 2024 15:51:29 -0500 Subject: [PATCH 029/428] create state vector on initialization --- .../lightning_qubit/lightning_qubit2.py | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index 348855b763..fd9ec58d77 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -55,14 +55,12 @@ PostprocessingFn = Callable[[ResultBatch], Result_or_ResultBatch] -def simulate(circuit: QuantumScript, state: LightningStateVector, dtype=np.complex128) -> Result: +def simulate(circuit: QuantumScript, state: LightningStateVector) -> Result: """Simulate a single quantum script. Args: circuit (QuantumTape): The single circuit to simulate state (LightningStateVector): handle to Lightning state vector - dtype: Datatypes for state-vector representation. Must be one of - ``np.complex64`` or ``np.complex128``. Returns: tuple(TensorLike): The results of the simulation @@ -75,7 +73,7 @@ def simulate(circuit: QuantumScript, state: LightningStateVector, dtype=np.compl return LightningMeasurements(final_state).measure_final_state(circuit) -def dummy_jacobian(circuit: QuantumTape): +def jacobian(circuit: QuantumTape): return np.array(0.0) @@ -203,13 +201,6 @@ def accepted_analytic_measurements(m: qml.measurements.MeasurementProcess) -> bo return isinstance(m, (qml.measurements.ExpectationMP)) -try: - # pylint: disable=import-error, no-name-in-module - from pennylane_lightning.lightning_qubit_ops import ( - StateVectorC64, - StateVectorC128, - ) - @simulator_tracking @convert_single_circuit_to_batch class LightningQubit2(Device): @@ -236,6 +227,8 @@ def __init__( # pylint: disable=too-many-arguments "To manually compile from source, follow the instructions at " "https://pennylane-lightning.readthedocs.io/en/latest/installation.html.") super().__init__(wires=wires, shots=shots) + + self._statevector = LightningStateVector(num_wires=len(self.wires), dtype=c_dtype) seed = np.random.randint(0, high=10000000) if seed == "global" else seed self._rng = np.random.default_rng(seed) @@ -331,7 +324,7 @@ def execute( results = [] for circuit in circuits: circuit = circuit.map_to_standard_wires() - results.append(simulate(circuit, **execution_config.device_options)) + results.append(simulate(circuit, self._statevector)) return tuple(results) From f50f83e20f252a00ff02fe314b96b4648ce08819 Mon Sep 17 00:00:00 2001 From: albi3ro Date: Fri, 16 Feb 2024 15:52:23 -0500 Subject: [PATCH 030/428] remove import of modifier from lightning --- pennylane_lightning/lightning_qubit/lightning_qubit2.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index fd9ec58d77..50a13134e4 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -33,7 +33,6 @@ from pennylane.tape import QuantumTape, QuantumScript from pennylane.transforms.core import TransformProgram from pennylane.typing import Result, ResultBatch -from pennylane_lightning.lightning_qubit.device_modifiers import convert_single_circuit_to_batch from ._state_vector import LightningStateVector from ._measurements import LightningMeasurements From 60b45ba2980b9a89f1103e13ea9ea7aeaf31d1ce Mon Sep 17 00:00:00 2001 From: Christina Lee Date: Fri, 16 Feb 2024 16:05:48 -0500 Subject: [PATCH 031/428] Update pennylane_lightning/lightning_qubit/lightning_qubit2.py --- pennylane_lightning/lightning_qubit/lightning_qubit2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index 50a13134e4..7a2abe590b 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -257,7 +257,7 @@ def c_dtype(self): return self._c_dtype @property - def operation(self) -> frozenset[str]: + def operations(self) -> frozenset[str]: """The names of the supported operations.""" return _operations From a4350b59fc8fe04a111791efd8933df29492a64e Mon Sep 17 00:00:00 2001 From: Amintor Dusko <87949283+AmintorDusko@users.noreply.github.com> Date: Wed, 21 Feb 2024 11:06:12 -0500 Subject: [PATCH 032/428] Update pennylane_lightning/lightning_qubit/_measurements.py Co-authored-by: Christina Lee --- pennylane_lightning/lightning_qubit/_measurements.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/_measurements.py b/pennylane_lightning/lightning_qubit/_measurements.py index ffc2a8912e..466493ac8e 100644 --- a/pennylane_lightning/lightning_qubit/_measurements.py +++ b/pennylane_lightning/lightning_qubit/_measurements.py @@ -164,9 +164,10 @@ def measure_final_state(self, circuit: QuantumScript) -> Result: Tuple[TensorLike]: The measurement results """ - if not circuit.shots: - # analytic case - if len(circuit.measurements) == 1: - return self.measurement(circuit.measurements[0]) + if circuit.shots: + raise NotImplementedError + # analytic case + if len(circuit.measurements) == 1: + return self.measurement(circuit.measurements[0]) - return tuple(self.measurement(mp) for mp in circuit.measurements) + return tuple(self.measurement(mp) for mp in circuit.measurements) From 80de99572788d5ef4367de08d1a41959ca7120f5 Mon Sep 17 00:00:00 2001 From: Amintor Dusko <87949283+AmintorDusko@users.noreply.github.com> Date: Wed, 21 Feb 2024 11:06:29 -0500 Subject: [PATCH 033/428] Update pennylane_lightning/lightning_qubit/_measurements.py Co-authored-by: Christina Lee --- pennylane_lightning/lightning_qubit/_measurements.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pennylane_lightning/lightning_qubit/_measurements.py b/pennylane_lightning/lightning_qubit/_measurements.py index 466493ac8e..3b0fe873f2 100644 --- a/pennylane_lightning/lightning_qubit/_measurements.py +++ b/pennylane_lightning/lightning_qubit/_measurements.py @@ -26,7 +26,6 @@ import numpy as np from typing import Callable, List -import pennylane as qml from pennylane.measurements import StateMeasurement, MeasurementProcess, ExpectationMP from pennylane.typing import TensorLike, Result from pennylane.tape import QuantumScript From af4535470aaf76871f057f1efcc71862f7d327b8 Mon Sep 17 00:00:00 2001 From: albi3ro Date: Wed, 21 Feb 2024 11:20:49 -0500 Subject: [PATCH 034/428] minor test updates --- .../lightning_qubit/lightning_qubit2.py | 15 ++--- tests/lightning_qubit2/test_expval_2.py | 57 +++++++++---------- 2 files changed, 32 insertions(+), 40 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index 7a2abe590b..8666a78aca 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -21,7 +21,7 @@ import pennylane as qml from pennylane.devices import Device, ExecutionConfig, DefaultExecutionConfig -from pennylane.devices.modifiers import convert_single_circuit_to_batch, simulator_tracking +from pennylane.devices.modifiers import single_tape_support, simulator_tracking from pennylane.devices.preprocess import ( decompose, validate_device_wires, @@ -67,8 +67,8 @@ def simulate(circuit: QuantumScript, state: LightningStateVector) -> Result: Note that this function can return measurements for non-commuting observables simultaneously. """ - final_state = state.reset_state() - final_state = final_state.get_final_state(circuit) + state.reset_state() + final_state = state.get_final_state(circuit) return LightningMeasurements(final_state).measure_final_state(circuit) @@ -195,13 +195,8 @@ def accepted_observables(obs: qml.operation.Operator) -> bool: return obs.name in _observables -def accepted_analytic_measurements(m: qml.measurements.MeasurementProcess) -> bool: - """Whether or not a state based measurement is supported by ``lightning.qubit``.""" - return isinstance(m, (qml.measurements.ExpectationMP)) - - @simulator_tracking -@convert_single_circuit_to_batch +@single_tape_support class LightningQubit2(Device): """PennyLane Lightning Qubit device. @@ -304,7 +299,7 @@ def supports_derivatives( def preprocess(self, execution_config: ExecutionConfig = DefaultExecutionConfig): program = TransformProgram() program.add_transform( - validate_measurements, analytic_measurements=accepted_analytic_measurements, name=self.name + validate_measurements, name=self.name ) program.add_transform(no_sampling) program.add_transform(validate_observables, accepted_observables, name=self.name) diff --git a/tests/lightning_qubit2/test_expval_2.py b/tests/lightning_qubit2/test_expval_2.py index 78553c3e44..17a1301538 100644 --- a/tests/lightning_qubit2/test_expval_2.py +++ b/tests/lightning_qubit2/test_expval_2.py @@ -34,14 +34,11 @@ VARPHI = np.linspace(0.02, 1, 3) +@pytest.mark.parametrize("c_dtype", (np.complex64, np.complex128)) @pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI))) class TestExpval: """Test expectation value calculations""" - @pytest.fixture(params=[np.complex64, np.complex128]) - def dev(self, request): - return LightningQubit(c_dtype=request.param) - @staticmethod def calculate_reference(tape): dev = DefaultQubit(max_workers=1) @@ -57,53 +54,52 @@ def process_and_execute(dev, tape): results = dev.execute(tapes) return transf_fn(results) - def test_Identity(self, theta, phi, dev, tol): + def test_Identity(self, theta, phi, c_dtype, tol): """Tests applying identities.""" - with qml.tape.QuantumTape() as tape: - qml.Identity(wires=[0]) - qml.Identity(wires=[0, 1]) - qml.Identity(wires=[1, 2]) - qml.RX(theta, wires=[0]) - qml.RX(phi, wires=[1]) - qml.expval(qml.PauliX(0)) - - calculated_val = self.process_and_execute(dev, tape) - reference_val = self.calculate_reference(tape) - + ops = [ + qml.Identity(0), + qml.Identity((0,1)), + qml.Identity((1,2)), + qml.RX(theta, 0), + qml.RX(phi, 1) + ] + measurements = [qml.expval(qml.PauliZ(0))] + tape = qml.tape.QuantumScript(ops, measurements) + + dev = LightningQubit(c_dtype=c_dtype, wires=3) + result = dev.execute(tape) + expected = np.cos(theta) tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 - assert np.allclose(calculated_val, reference_val, tol) + assert np.allclose(result, expected, tol) - def test_identity_expectation(self, theta, phi, dev, tol): - """Tests identity.""" + def test_identity_expectation(self, theta, phi, c_dtype, tol): + """Tests identity expectations.""" + dev = LightningQubit(wires=2, c_dtype=c_dtype) tape = qml.tape.QuantumScript( [qml.RX(theta, wires=[0]), qml.RX(phi, wires=[1]), qml.CNOT(wires=[0, 1])], [qml.expval(qml.Identity(wires=[0])), qml.expval(qml.Identity(wires=[1]))], ) - - calculated_val = self.process_and_execute(dev, tape) - reference_val = self.calculate_reference(tape) + result = dev.execute(tape) tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 - assert np.allclose(calculated_val, reference_val, tol) + assert np.allclose(1.0, result, tol) - def test_multi_wire_identity_expectation(self, theta, phi, dev, tol): + def test_multi_wire_identity_expectation(self, theta, phi, c_dtype, tol): """Tests multi-wire identity.""" + dev = LightningQubit(wires=2, c_dtype=c_dtype) tape = qml.tape.QuantumScript( [qml.RX(theta, wires=[0]), qml.RX(phi, wires=[1]), qml.CNOT(wires=[0, 1])], [qml.expval(qml.Identity(wires=[0, 1]))], ) - - calculated_val = self.process_and_execute(dev, tape) - reference_val = self.calculate_reference(tape) - + result = dev.execute(tape) tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 - assert np.allclose(calculated_val, reference_val, tol) + assert np.allclose(1.0, result, tol) @pytest.mark.parametrize( "wires", @@ -113,9 +109,10 @@ def test_multi_wire_identity_expectation(self, theta, phi, dev, tol): (["b", "a"]), ], ) - def test_PauliZ_expectation(self, theta, phi, dev, tol, wires): + def test_PauliZ_expectation(self, theta, phi, c_dtype, tol, wires): """Tests PauliZ.""" + dev = LightningQubit(wires=wires, c_dtype=c_dtype) tape = qml.tape.QuantumScript( [qml.RX(theta, wires=wires[0]), qml.RX(phi, wires=wires[1]), qml.CNOT(wires=wires)], [qml.expval(qml.PauliZ(wires=wires[0])), qml.expval(qml.PauliZ(wires=wires[1]))], From dc3149f864b0074e4b16627fbbeae306a86654ef Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Wed, 21 Feb 2024 11:23:05 -0500 Subject: [PATCH 035/428] fix reset state --- pennylane_lightning/lightning_qubit/lightning_qubit2.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index 08a3a28251..6b32d644c2 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -56,8 +56,8 @@ def simulate(circuit: QuantumScript, state: LightningStateVector, dtype=np.compl Note that this function can return measurements for non-commuting observables simultaneously. """ - final_state = state.reset_state() - final_state = final_state.get_final_state(circuit) + state.reset_state() + final_state = state.get_final_state(circuit) return LightningMeasurements(final_state).measure_final_state(circuit) From 378e3763fa4704cd1e5fb6bd4afc45e626a9fdcf Mon Sep 17 00:00:00 2001 From: Amintor Dusko <87949283+AmintorDusko@users.noreply.github.com> Date: Wed, 21 Feb 2024 12:57:43 -0500 Subject: [PATCH 036/428] Update pennylane_lightning/lightning_qubit/_state_vector.py Co-authored-by: Christina Lee --- pennylane_lightning/lightning_qubit/_state_vector.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pennylane_lightning/lightning_qubit/_state_vector.py b/pennylane_lightning/lightning_qubit/_state_vector.py index 66c1d410b6..cc0e4a0d2d 100644 --- a/pennylane_lightning/lightning_qubit/_state_vector.py +++ b/pennylane_lightning/lightning_qubit/_state_vector.py @@ -169,7 +169,6 @@ def _preprocess_state_vector(self, state, device_wires): """ # translate to wire labels used by device - device_wires = self.map_wires(device_wires) # special case for integral types if state.dtype.kind == "i": From 7a5c01020fada181fceae3e9fefa1a48fe7c89c7 Mon Sep 17 00:00:00 2001 From: Amintor Dusko <87949283+AmintorDusko@users.noreply.github.com> Date: Wed, 21 Feb 2024 12:58:02 -0500 Subject: [PATCH 037/428] Update pennylane_lightning/lightning_qubit/_state_vector.py Co-authored-by: Christina Lee --- pennylane_lightning/lightning_qubit/_state_vector.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pennylane_lightning/lightning_qubit/_state_vector.py b/pennylane_lightning/lightning_qubit/_state_vector.py index cc0e4a0d2d..8aa22c77e1 100644 --- a/pennylane_lightning/lightning_qubit/_state_vector.py +++ b/pennylane_lightning/lightning_qubit/_state_vector.py @@ -201,7 +201,6 @@ def _get_basis_state_index(self, state, wires): int: basis state index """ # translate to wire labels used by device - device_wires = self.map_wires(wires) # length of basis state parameter n_basis_state = len(state) From b23eabf80fe95040bd78cb7a9537068f6020bff8 Mon Sep 17 00:00:00 2001 From: Amintor Dusko <87949283+AmintorDusko@users.noreply.github.com> Date: Wed, 21 Feb 2024 12:59:18 -0500 Subject: [PATCH 038/428] Update pennylane_lightning/lightning_qubit/_state_vector.py Co-authored-by: Christina Lee --- pennylane_lightning/lightning_qubit/_state_vector.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/_state_vector.py b/pennylane_lightning/lightning_qubit/_state_vector.py index 8aa22c77e1..8feeae0727 100644 --- a/pennylane_lightning/lightning_qubit/_state_vector.py +++ b/pennylane_lightning/lightning_qubit/_state_vector.py @@ -172,8 +172,8 @@ def _preprocess_state_vector(self, state, device_wires): # special case for integral types if state.dtype.kind == "i": - state = qml.numpy.array(state, dtype=self.C_DTYPE) - state = self._asarray(state, dtype=self.C_DTYPE) + state = qml.numpy.array(state, dtype=self.dtype) + state = self._asarray(state, dtype=self.dtype) if len(device_wires) == self.num_wires and Wires(sorted(device_wires)) == device_wires: return None, state From 112d2ffc5762cd771d7af9b227875f406918f9f4 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Wed, 21 Feb 2024 15:11:10 -0500 Subject: [PATCH 039/428] remove LightningQubit2 references --- pennylane_lightning/lightning_qubit/__init__.py | 1 - tests/conftest.py | 7 +------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/__init__.py b/pennylane_lightning/lightning_qubit/__init__.py index 9d8ddab62d..53e50cbf00 100644 --- a/pennylane_lightning/lightning_qubit/__init__.py +++ b/pennylane_lightning/lightning_qubit/__init__.py @@ -15,4 +15,3 @@ from pennylane_lightning.core import __version__ from .lightning_qubit import LightningQubit -from .lightning_qubit2 import LightningQubit2 diff --git a/tests/conftest.py b/tests/conftest.py index 25b7ada6e6..d04117520c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -87,7 +87,7 @@ def n_subsystems(request): # Looking for the device for testing. default_device = "lightning.qubit" -supported_devices = {"lightning.kokkos", "lightning.qubit", "lightning.qubit2", "lightning.gpu"} +supported_devices = {"lightning.kokkos", "lightning.qubit", "lightning.gpu"} supported_devices.update({sb.replace(".", "_") for sb in supported_devices}) @@ -131,11 +131,6 @@ def get_device(): if hasattr(pennylane_lightning, "lightning_gpu_ops"): import pennylane_lightning.lightning_gpu_ops as lightning_ops -elif device_name == "lightning.qubit2": - from pennylane_lightning.lightning_qubit import LightningQubit2 as LightningDevice - - if hasattr(pennylane_lightning, "lightning_qubit_ops"): - import pennylane_lightning.lightning_qubit_ops as lightning_ops else: from pennylane_lightning.lightning_qubit import LightningQubit as LightningDevice From 1a8fe19731dbdbbd5b4edf8c3581edad18796dcb Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Wed, 21 Feb 2024 15:15:11 -0500 Subject: [PATCH 040/428] remove unnecessary modules --- pennylane_lightning/lightning_qubit/lightning_qubit2.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index 6b32d644c2..fb28c8cfbd 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -15,13 +15,8 @@ This module contains the LightningQubit2 class that inherits from the new device interface. """ -from typing import Union, Sequence, Optional -from dataclasses import replace import numpy as np -import pennylane as qml - - from pennylane.tape import QuantumScript from pennylane.typing import Result From 9ded7989af699c6f9a4bb2a046252d850ec61175 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Wed, 21 Feb 2024 15:15:57 -0500 Subject: [PATCH 041/428] merging Serializer classes --- pennylane_lightning/core/_serialize.py | 48 ++- .../lightning_qubit/_serialize.py | 390 ------------------ 2 files changed, 27 insertions(+), 411 deletions(-) delete mode 100644 pennylane_lightning/lightning_qubit/_serialize.py diff --git a/pennylane_lightning/core/_serialize.py b/pennylane_lightning/core/_serialize.py index 5e4a37213c..50810608c7 100644 --- a/pennylane_lightning/core/_serialize.py +++ b/pennylane_lightning/core/_serialize.py @@ -171,26 +171,26 @@ def sparse_hamiltonian_obs(self): ) return self.sparse_hamiltonian_c64 if self.use_csingle else self.sparse_hamiltonian_c128 - def _named_obs(self, observable, wires_map: dict): + def _named_obs(self, observable, wires_map: dict = None): """Serializes a Named observable""" - wires = [wires_map[w] for w in observable.wires] + wires = [wires_map[w] for w in observable.wires] if wires_map else observable.wires.tolist() if observable.name == "Identity": wires = wires[:1] return self.named_obs(observable.name, wires) - def _hermitian_ob(self, observable, wires_map: dict): + def _hermitian_ob(self, observable, wires_map: dict = None): """Serializes a Hermitian observable""" assert not isinstance(observable, Tensor) - wires = [wires_map[w] for w in observable.wires] + wires = [wires_map[w] for w in observable.wires] if wires_map else observable.wires.tolist() return self.hermitian_obs(matrix(observable).ravel().astype(self.ctype), wires) - def _tensor_ob(self, observable, wires_map: dict): + def _tensor_ob(self, observable, wires_map: dict = None): """Serialize a tensor observable""" assert isinstance(observable, Tensor) return self.tensor_obs([self._ob(obs, wires_map) for obs in observable.obs]) - def _hamiltonian(self, observable, wires_map: dict): + def _hamiltonian(self, observable, wires_map: dict = None): coeffs = np.array(unwrap(observable.coeffs)).astype(self.rtype) terms = [self._ob(t, wires_map) for t in observable.ops] @@ -199,7 +199,7 @@ def _hamiltonian(self, observable, wires_map: dict): return self.hamiltonian_obs(coeffs, terms) - def _sparse_hamiltonian(self, observable, wires_map: dict): + def _sparse_hamiltonian(self, observable, wires_map: dict = None): """Serialize an observable (Sparse Hamiltonian) Args: @@ -214,7 +214,7 @@ def _sparse_hamiltonian(self, observable, wires_map: dict): Hmat = Hamiltonian([1.0], [Identity(0)]).sparse_matrix() H_sparse = SparseHamiltonian(Hmat, wires=range(1)) spm = H_sparse.sparse_matrix() - # Only root 0 needs the overall sparsematrix data + # Only root 0 needs the overall sparse matrix data if self._mpi_manager().getRank() == 0: spm = observable.sparse_matrix() self._mpi_manager().Barrier() @@ -224,26 +224,28 @@ def _sparse_hamiltonian(self, observable, wires_map: dict): indices = np.array(spm.indices).astype(np.int64) offsets = np.array(spm.indptr).astype(np.int64) - wires = [] - wires_list = observable.wires.tolist() - wires.extend([wires_map[w] for w in wires_list]) + wires = [wires_map[w] for w in observable.wires] if wires_map else observable.wires.tolist() return self.sparse_hamiltonian_obs(data, indices, offsets, wires) - def _pauli_word(self, observable, wires_map: dict): + def _pauli_word(self, observable, wires_map: dict = None): """Serialize a :class:`pennylane.pauli.PauliWord` into a Named or Tensor observable.""" + + def map_wire(wire: int): + return wires_map[wire] if wires_map else wire + if len(observable) == 1: wire, pauli = list(observable.items())[0] - return self.named_obs(pauli_name_map[pauli], [wires_map[wire]]) + return self.named_obs(pauli_name_map[pauli], [map_wire(wire)]) return self.tensor_obs( [ - self.named_obs(pauli_name_map[pauli], [wires_map[wire]]) + self.named_obs(pauli_name_map[pauli], [map_wire(wire)]) for wire, pauli in observable.items() ] ) - def _pauli_sentence(self, observable, wires_map: dict): + def _pauli_sentence(self, observable, wires_map: dict = None): """Serialize a :class:`pennylane.pauli.PauliSentence` into a Hamiltonian.""" pwords, coeffs = zip(*observable.items()) terms = [self._pauli_word(pw, wires_map) for pw in pwords] @@ -254,7 +256,7 @@ def _pauli_sentence(self, observable, wires_map: dict): return self.hamiltonian_obs(coeffs, terms) # pylint: disable=protected-access - def _ob(self, observable, wires_map): + def _ob(self, observable, wires_map: dict = None): """Serialize a :class:`pennylane.operation.Observable` into an Observable.""" if isinstance(observable, Tensor): return self._tensor_ob(observable, wires_map) @@ -268,7 +270,7 @@ def _ob(self, observable, wires_map): return self._pauli_sentence(observable._pauli_rep, wires_map) return self._hermitian_ob(observable, wires_map) - def serialize_observables(self, tape: QuantumTape, wires_map: dict) -> List: + def serialize_observables(self, tape: QuantumTape, wires_map: dict = None) -> List: """Serializes the observables of an input tape. Args: @@ -294,7 +296,7 @@ def serialize_observables(self, tape: QuantumTape, wires_map: dict) -> List: return serialized_obs, offset_indices def serialize_ops( - self, tape: QuantumTape, wires_map: dict + self, tape: QuantumTape, wires_map: dict = None ) -> Tuple[ List[List[str]], List[np.ndarray], @@ -348,7 +350,7 @@ def get_wires(operation, single_op): wires_list = single_op.wires.tolist() controlled_wires_list = [] control_values_list = [] - return single_op, name, wires_list, controlled_wires_list, control_values_list + return single_op, name, list(wires_list), controlled_wires_list, control_values_list for operation in tape.operations: if isinstance(operation, (BasisState, StatePrep)): @@ -381,8 +383,12 @@ def get_wires(operation, single_op): mats.append([]) controlled_values.append(controlled_values_list) - controlled_wires.append([wires_map[w] for w in controlled_wires_list]) - wires.append([wires_map[w] for w in wires_list]) + controlled_wires.append( + [wires_map[w] for w in controlled_wires_list] + if wires_map + else list(controlled_wires_list) + ) + wires.append([wires_map[w] for w in wires_list] if wires_map else wires_list) inverses = [False] * len(names) return ( diff --git a/pennylane_lightning/lightning_qubit/_serialize.py b/pennylane_lightning/lightning_qubit/_serialize.py deleted file mode 100644 index 2553126c6f..0000000000 --- a/pennylane_lightning/lightning_qubit/_serialize.py +++ /dev/null @@ -1,390 +0,0 @@ -# Copyright 2018-2024 Xanadu Quantum Technologies Inc. - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -r""" -Helper functions for serializing quantum tapes. -""" -from typing import List, Tuple -import numpy as np -from pennylane import ( - BasisState, - Hadamard, - PauliX, - PauliY, - PauliZ, - Identity, - StatePrep, - Rot, - Hamiltonian, - SparseHamiltonian, - QubitUnitary, -) -from pennylane.operation import Tensor -from pennylane.tape import QuantumTape -from pennylane.math import unwrap - -from pennylane import matrix, DeviceError - -pauli_name_map = { - "I": "Identity", - "X": "PauliX", - "Y": "PauliY", - "Z": "PauliZ", -} - - -class QuantumScriptSerializer: - """Serializer class for `pennylane.tape.QuantumScript` data. - - Args: - device_name: device shortname. - use_csingle (bool): whether to use np.complex64 instead of np.complex128 - - """ - - # pylint: disable=import-outside-toplevel, too-many-instance-attributes - def __init__( - self, device_name, use_csingle: bool = False, use_mpi: bool = False, split_obs: bool = False - ): - self.use_csingle = use_csingle - self.device_name = device_name - self.split_obs = split_obs - if device_name == "lightning.qubit" or device_name == "lightning.qubit2": - try: - import pennylane_lightning.lightning_qubit_ops as lightning_ops - except ImportError as exception: - raise ImportError( - f"Pre-compiled binaries for {device_name}" - " serialize functionality are not available." - ) from exception - elif device_name == "lightning.kokkos": - try: - import pennylane_lightning.lightning_kokkos_ops as lightning_ops - except ImportError as exception: - raise ImportError( - f"Pre-compiled binaries for {device_name}" - " serialize functionality are not available." - ) from exception - elif device_name == "lightning.gpu": - try: - import pennylane_lightning.lightning_gpu_ops as lightning_ops - except ImportError as exception: - raise ImportError( - f"Pre-compiled binaries for {device_name} are not available." - ) from exception - else: - raise DeviceError(f'The device name "{device_name}" is not a valid option.') - self.statevector_c64 = lightning_ops.StateVectorC64 - self.statevector_c128 = lightning_ops.StateVectorC128 - self.named_obs_c64 = lightning_ops.observables.NamedObsC64 - self.named_obs_c128 = lightning_ops.observables.NamedObsC128 - self.hermitian_obs_c64 = lightning_ops.observables.HermitianObsC64 - self.hermitian_obs_c128 = lightning_ops.observables.HermitianObsC128 - self.tensor_prod_obs_c64 = lightning_ops.observables.TensorProdObsC64 - self.tensor_prod_obs_c128 = lightning_ops.observables.TensorProdObsC128 - self.hamiltonian_c64 = lightning_ops.observables.HamiltonianC64 - self.hamiltonian_c128 = lightning_ops.observables.HamiltonianC128 - self.sparse_hamiltonian_c64 = lightning_ops.observables.SparseHamiltonianC64 - self.sparse_hamiltonian_c128 = lightning_ops.observables.SparseHamiltonianC128 - - self._use_mpi = use_mpi - - if self._use_mpi: - self.statevector_mpi_c64 = lightning_ops.StateVectorMPIC64 - self.statevector_mpi_c128 = lightning_ops.StateVectorMPIC128 - self.named_obs_mpi_c64 = lightning_ops.observablesMPI.NamedObsMPIC64 - self.named_obs_mpi_c128 = lightning_ops.observablesMPI.NamedObsMPIC128 - self.hermitian_obs_mpi_c64 = lightning_ops.observablesMPI.HermitianObsMPIC64 - self.hermitian_obs_mpi_c128 = lightning_ops.observablesMPI.HermitianObsMPIC128 - self.tensor_prod_obs_mpi_c64 = lightning_ops.observablesMPI.TensorProdObsMPIC64 - self.tensor_prod_obs_mpi_c128 = lightning_ops.observablesMPI.TensorProdObsMPIC128 - self.hamiltonian_mpi_c64 = lightning_ops.observablesMPI.HamiltonianMPIC64 - self.hamiltonian_mpi_c128 = lightning_ops.observablesMPI.HamiltonianMPIC128 - self.sparse_hamiltonian_mpi_c64 = lightning_ops.observablesMPI.SparseHamiltonianMPIC64 - self.sparse_hamiltonian_mpi_c128 = lightning_ops.observablesMPI.SparseHamiltonianMPIC128 - - self._mpi_manager = lightning_ops.MPIManager - - @property - def ctype(self): - """Complex type.""" - return np.complex64 if self.use_csingle else np.complex128 - - @property - def rtype(self): - """Real type.""" - return np.float32 if self.use_csingle else np.float64 - - @property - def sv_type(self): - """State vector matching ``use_csingle`` precision (and MPI if it is supported).""" - if self._use_mpi: - return self.statevector_mpi_c64 if self.use_csingle else self.statevector_mpi_c128 - return self.statevector_c64 if self.use_csingle else self.statevector_c128 - - @property - def named_obs(self): - """Named observable matching ``use_csingle`` precision.""" - if self._use_mpi: - return self.named_obs_mpi_c64 if self.use_csingle else self.named_obs_mpi_c128 - return self.named_obs_c64 if self.use_csingle else self.named_obs_c128 - - @property - def hermitian_obs(self): - """Hermitian observable matching ``use_csingle`` precision.""" - if self._use_mpi: - return self.hermitian_obs_mpi_c64 if self.use_csingle else self.hermitian_obs_mpi_c128 - return self.hermitian_obs_c64 if self.use_csingle else self.hermitian_obs_c128 - - @property - def tensor_obs(self): - """Tensor product observable matching ``use_csingle`` precision.""" - if self._use_mpi: - return ( - self.tensor_prod_obs_mpi_c64 if self.use_csingle else self.tensor_prod_obs_mpi_c128 - ) - return self.tensor_prod_obs_c64 if self.use_csingle else self.tensor_prod_obs_c128 - - @property - def hamiltonian_obs(self): - """Hamiltonian observable matching ``use_csingle`` precision.""" - if self._use_mpi: - return self.hamiltonian_mpi_c64 if self.use_csingle else self.hamiltonian_mpi_c128 - return self.hamiltonian_c64 if self.use_csingle else self.hamiltonian_c128 - - @property - def sparse_hamiltonian_obs(self): - """SparseHamiltonian observable matching ``use_csingle`` precision.""" - if self._use_mpi: - return ( - self.sparse_hamiltonian_mpi_c64 - if self.use_csingle - else self.sparse_hamiltonian_mpi_c128 - ) - return self.sparse_hamiltonian_c64 if self.use_csingle else self.sparse_hamiltonian_c128 - - def _named_obs(self, observable): - """Serializes a Named observable""" - wires = observable.wires.tolist() - if observable.name == "Identity": - wires = wires[:1] - return self.named_obs(observable.name, wires) - - def _hermitian_ob(self, observable): - """Serializes a Hermitian observable""" - assert not isinstance(observable, Tensor) - - return self.hermitian_obs( - matrix(observable).ravel().astype(self.ctype), observable.wires.tolist() - ) - - def _tensor_ob(self, observable): - """Serialize a tensor observable""" - assert isinstance(observable, Tensor) - return self.tensor_obs([self._ob(obs) for obs in observable.obs]) - - def _hamiltonian(self, observable): - coeffs = np.array(unwrap(observable.coeffs)).astype(self.rtype) - terms = [self._ob(t) for t in observable.ops] - - if self.split_obs: - return [self.hamiltonian_obs([c], [t]) for (c, t) in zip(coeffs, terms)] - - return self.hamiltonian_obs(coeffs, terms) - - def _sparse_hamiltonian(self, observable): - """Serialize an observable (Sparse Hamiltonian) - - Args: - observable (Observable): the input observable (Sparse Hamiltonian) - wire_map (dict): a dictionary mapping input wires to the device's backend wires - - Returns: - sparse_hamiltonian_obs (SparseHamiltonianC64 or SparseHamiltonianC128): A Sparse Hamiltonian observable object compatible with the C++ backend - """ - - if self._use_mpi: - Hmat = Hamiltonian([1.0], [Identity(0)]).sparse_matrix() - H_sparse = SparseHamiltonian(Hmat, wires=range(1)) - spm = H_sparse.sparse_matrix() - # Only root 0 needs the overall sparse matrix data - if self._mpi_manager().getRank() == 0: - spm = observable.sparse_matrix() - self._mpi_manager().Barrier() - else: - spm = observable.sparse_matrix() - data = np.array(spm.data).astype(self.ctype) - indices = np.array(spm.indices).astype(np.int64) - offsets = np.array(spm.indptr).astype(np.int64) - - return self.sparse_hamiltonian_obs(data, indices, offsets, observable.wires.tolist()) - - def _pauli_word(self, observable): - """Serialize a :class:`pennylane.pauli.PauliWord` into a Named or Tensor observable.""" - if len(observable) == 1: - wire, pauli = list(observable.items())[0] - return self.named_obs(pauli_name_map[pauli], [wire]) - - return self.tensor_obs( - [self.named_obs(pauli_name_map[pauli], [wire]) for wire, pauli in observable.items()] - ) - - def _pauli_sentence(self, observable): - """Serialize a :class:`pennylane.pauli.PauliSentence` into a Hamiltonian.""" - pwords, coeffs = zip(*observable.items()) - terms = [self._pauli_word(pw) for pw in pwords] - coeffs = np.array(coeffs).astype(self.rtype) - - if self.split_obs: - return [self.hamiltonian_obs([c], [t]) for (c, t) in zip(coeffs, terms)] - return self.hamiltonian_obs(coeffs, terms) - - # pylint: disable=protected-access - def _ob(self, observable): - """Serialize a :class:`pennylane.operation.Observable` into an Observable.""" - if isinstance(observable, Tensor): - return self._tensor_ob(observable) - if observable.name == "Hamiltonian": - return self._hamiltonian(observable) - if observable.name == "SparseHamiltonian": - return self._sparse_hamiltonian(observable) - if isinstance(observable, (PauliX, PauliY, PauliZ, Identity, Hadamard)): - return self._named_obs(observable) - if observable._pauli_rep is not None: - return self._pauli_sentence(observable._pauli_rep) - return self._hermitian_ob(observable) - - def serialize_observables(self, tape: QuantumTape) -> List: - """Serializes the observables of an input tape. - - Args: - tape (QuantumTape): the input quantum tape - - Returns: - list(ObsStructC128 or ObsStructC64): A list of observable objects compatible with - the C++ backend - """ - - serialized_obs = [] - offset_indices = [0] - - for observable in tape.observables: - ser_ob = self._ob(observable) - if isinstance(ser_ob, list): - serialized_obs.extend(ser_ob) - offset_indices.append(offset_indices[-1] + len(ser_ob)) - else: - serialized_obs.append(ser_ob) - offset_indices.append(offset_indices[-1] + 1) - return serialized_obs, offset_indices - - def serialize_ops( - self, tape: QuantumTape - ) -> Tuple[ - List[List[str]], - List[np.ndarray], - List[List[int]], - List[bool], - List[np.ndarray], - List[List[int]], - List[List[bool]], - ]: - """Serializes the operations of an input tape. - - The state preparation operations are not included. - - Args: - tape (QuantumTape): the input quantum tape - - Returns: - Tuple[list, list, list, list, list]: A serialization of the operations, containing a - list of operation names, a list of operation parameters, a list of observable wires, - a list of inverses, a list of matrices for the operations that do not have a - dedicated kernel, a list of controlled wires and a list of controlled values. - """ - names = [] - params = [] - controlled_wires = [] - controlled_values = [] - wires = [] - mats = [] - - uses_stateprep = False - - def get_wires(operation, single_op): - if operation.name[0:2] == "C(" or operation.name == "MultiControlledX": - name = "PauliX" if operation.name == "MultiControlledX" else operation.base.name - controlled_wires_list = operation.control_wires - if operation.name == "MultiControlledX": - wires_list = list(set(operation.wires) - set(controlled_wires_list)) - else: - wires_list = operation.target_wires - control_values_list = ( - [bool(int(i)) for i in operation.hyperparameters["control_values"]] - if operation.name == "MultiControlledX" - else operation.control_values - ) - if not hasattr(self.sv_type, name): - single_op = QubitUnitary(matrix(single_op.base), single_op.base.wires) - name = single_op.name - else: - name = single_op.name - wires_list = single_op.wires.tolist() - controlled_wires_list = [] - control_values_list = [] - return single_op, name, list(wires_list), controlled_wires_list, control_values_list - - for operation in tape.operations: - if isinstance(operation, (BasisState, StatePrep)): - uses_stateprep = True - continue - if isinstance(operation, Rot): - op_list = operation.expand().operations - else: - op_list = [operation] - - for single_op in op_list: - ( - single_op, - name, - wires_list, - controlled_wires_list, - controlled_values_list, - ) = get_wires(operation, single_op) - names.append(name) - # QubitUnitary is a special case, it has a parameter which is not differentiable. - # We thus pass a dummy 0.0 parameter which will not be referenced - if name == "QubitUnitary": - params.append([0.0]) - mats.append(matrix(single_op)) - elif not hasattr(self.sv_type, name): - params.append([]) - mats.append(matrix(single_op)) - else: - params.append(single_op.parameters) - mats.append([]) - - controlled_values.append(controlled_values_list) - controlled_wires.append(list(controlled_wires_list)) - wires.append(wires_list) - - inverses = [False] * len(names) - return ( - names, - params, - wires, - inverses, - mats, - controlled_wires, - controlled_values, - ), uses_stateprep From 2e43fd663f444b7046be4eeaef7d4fdb05fa9f2b Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Wed, 21 Feb 2024 15:16:20 -0500 Subject: [PATCH 042/428] update serialize tests --- tests/test_serialize.py | 83 ++++++++++++++++++++++++----------------- 1 file changed, 49 insertions(+), 34 deletions(-) diff --git a/tests/test_serialize.py b/tests/test_serialize.py index d4bba3cce1..01ba41dbbd 100644 --- a/tests/test_serialize.py +++ b/tests/test_serialize.py @@ -105,10 +105,11 @@ def test_wrong_device_name(): ], ) def test_obs_returns_expected_type(obs, obs_type): - """Tests that observables get serialized to the expected type.""" + """Tests that observables get serialized to the expected type, with and without wires map""" assert isinstance( QuantumScriptSerializer(device_name)._ob(obs, dict(enumerate(obs.wires))), obs_type ) + assert isinstance(QuantumScriptSerializer(device_name)._ob(obs), obs_type) class TestSerializeObs: @@ -117,7 +118,8 @@ class TestSerializeObs: wires_dict = {i: i for i in range(10)} @pytest.mark.parametrize("use_csingle", [True, False]) - def test_tensor_non_tensor_return(self, use_csingle): + @pytest.mark.parametrize("wires_map", [wires_dict, None]) + def test_tensor_non_tensor_return(self, use_csingle, wires_map): """Test expected serialization for a mixture of tensor product and non-tensor product return""" with qml.tape.QuantumTape() as tape: @@ -127,19 +129,19 @@ def test_tensor_non_tensor_return(self, use_csingle): tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 named_obs = NamedObsC64 if use_csingle else NamedObsC128 - s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables( - tape, self.wires_dict - ) - s_expected = [ tensor_prod_obs([named_obs("PauliZ", [0]), named_obs("PauliX", [1])]), named_obs("Hadamard", [1]), ] + s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables( + tape, wires_map + ) assert s == s_expected @pytest.mark.parametrize("use_csingle", [True, False]) - def test_hermitian_return(self, use_csingle): + @pytest.mark.parametrize("wires_map", [wires_dict, None]) + def test_hermitian_return(self, use_csingle, wires_map): """Test expected serialization for a Hermitian return""" with qml.tape.QuantumTape() as tape: qml.expval(qml.Hermitian(np.eye(4), wires=[0, 1])) @@ -148,7 +150,7 @@ def test_hermitian_return(self, use_csingle): c_dtype = np.complex64 if use_csingle else np.complex128 s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables( - tape, self.wires_dict + tape, wires_map ) s_expected = hermitian_obs( np.array( @@ -160,7 +162,8 @@ def test_hermitian_return(self, use_csingle): assert s[0] == s_expected @pytest.mark.parametrize("use_csingle", [True, False]) - def test_hermitian_tensor_return(self, use_csingle): + @pytest.mark.parametrize("wires_map", [wires_dict, None]) + def test_hermitian_tensor_return(self, use_csingle, wires_map): """Test expected serialization for a Hermitian return""" with qml.tape.QuantumTape() as tape: qml.expval(qml.Hermitian(np.eye(4), wires=[0, 1]) @ qml.Hermitian(np.eye(2), wires=[2])) @@ -169,7 +172,7 @@ def test_hermitian_tensor_return(self, use_csingle): tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables( - tape, self.wires_dict + tape, wires_map ) s_expected = tensor_prod_obs( @@ -182,7 +185,8 @@ def test_hermitian_tensor_return(self, use_csingle): assert s[0] == s_expected @pytest.mark.parametrize("use_csingle", [True, False]) - def test_mixed_tensor_return(self, use_csingle): + @pytest.mark.parametrize("wires_map", [wires_dict, None]) + def test_mixed_tensor_return(self, use_csingle, wires_map): """Test expected serialization for a mixture of Hermitian and Pauli return""" with qml.tape.QuantumTape() as tape: qml.expval(qml.Hermitian(np.eye(4), wires=[0, 1]) @ qml.PauliY(2)) @@ -193,7 +197,7 @@ def test_mixed_tensor_return(self, use_csingle): named_obs = NamedObsC64 if use_csingle else NamedObsC128 s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables( - tape, self.wires_dict + tape, wires_map ) s_expected = tensor_prod_obs( @@ -203,7 +207,8 @@ def test_mixed_tensor_return(self, use_csingle): assert s[0] == s_expected @pytest.mark.parametrize("use_csingle", [True, False]) - def test_hamiltonian_return(self, use_csingle): + @pytest.mark.parametrize("wires_map", [wires_dict, None]) + def test_hamiltonian_return(self, use_csingle, wires_map): """Test expected serialization for a Hamiltonian return""" ham = qml.Hamiltonian( @@ -226,7 +231,7 @@ def test_hamiltonian_return(self, use_csingle): c_dtype = np.complex64 if use_csingle else np.complex128 s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables( - tape, self.wires_dict + tape, wires_map ) s_expected = hamiltonian_obs( @@ -246,7 +251,8 @@ def test_hamiltonian_return(self, use_csingle): assert s[0] == s_expected @pytest.mark.parametrize("use_csingle", [True, False]) - def test_hamiltonian_tensor_return(self, use_csingle): + @pytest.mark.parametrize("wires_map", [wires_dict, None]) + def test_hamiltonian_tensor_return(self, use_csingle, wires_map): """Test expected serialization for a Hamiltonian return""" with qml.tape.QuantumTape() as tape: @@ -268,7 +274,7 @@ def test_hamiltonian_tensor_return(self, use_csingle): c_dtype = np.complex64 if use_csingle else np.complex128 s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables( - tape, self.wires_dict + tape, wires_map ) # Expression (ham @ obs) is converted internally by Pennylane @@ -295,7 +301,8 @@ def test_hamiltonian_tensor_return(self, use_csingle): assert s[0] == s_expected @pytest.mark.parametrize("use_csingle", [True, False]) - def test_hamiltonian_mix_return(self, use_csingle): + @pytest.mark.parametrize("wires_map", [wires_dict, None]) + def test_hamiltonian_mix_return(self, use_csingle, wires_map): """Test expected serialization for a Hamiltonian return""" ham1 = qml.Hamiltonian( @@ -323,7 +330,7 @@ def test_hamiltonian_mix_return(self, use_csingle): c_dtype = np.complex64 if use_csingle else np.complex128 s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables( - tape, self.wires_dict + tape, wires_map ) s_expected1 = hamiltonian_obs( @@ -371,11 +378,12 @@ def test_hamiltonian_mix_return(self, use_csingle): ], ) @pytest.mark.parametrize("use_csingle", [True, False]) - def test_op_arithmetic_uses_hamiltonian(self, use_csingle, obs, coeffs, terms): + @pytest.mark.parametrize("wires_map", [wires_dict, None]) + def test_op_arithmetic_uses_hamiltonian(self, use_csingle, obs, coeffs, terms, wires_map): """Tests that an arithmetic obs with a PauliRep serializes as a Hamiltonian.""" tape = qml.tape.QuantumTape(measurements=[qml.expval(obs)]) res, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables( - tape, self.wires_dict + tape, wires_map ) assert len(res) == 1 assert isinstance(res[0], HamiltonianC64 if use_csingle else HamiltonianC128) @@ -397,11 +405,12 @@ def test_op_arithmetic_uses_hamiltonian(self, use_csingle, obs, coeffs, terms): assert res[0] == hamiltonian_obs(coeffs, expected_terms) @pytest.mark.parametrize("use_csingle", [True, False]) - def test_multi_wire_identity(self, use_csingle): + @pytest.mark.parametrize("wires_map", [wires_dict, None]) + def test_multi_wire_identity(self, use_csingle, wires_map): """Tests that multi-wire Identity does not fail serialization.""" tape = qml.tape.QuantumTape(measurements=[qml.expval(qml.Identity(wires=[1, 2]))]) res, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables( - tape, self.wires_dict + tape, wires_map ) assert len(res) == 1 @@ -414,14 +423,15 @@ class TestSerializeOps: wires_dict = {i: i for i in range(10)} - def test_basic_circuit(self): + @pytest.mark.parametrize("wires_map", [wires_dict, None]) + def test_basic_circuit(self, wires_map): """Test expected serialization for a simple circuit""" with qml.tape.QuantumTape() as tape: qml.RX(0.4, wires=0) qml.RY(0.6, wires=1) qml.CNOT(wires=[0, 1]) - s = QuantumScriptSerializer(device_name).serialize_ops(tape, self.wires_dict) + s = QuantumScriptSerializer(device_name).serialize_ops(tape, wires_map) s_expected = ( ( ["RX", "RY", "CNOT"], @@ -436,7 +446,8 @@ def test_basic_circuit(self): ) assert s == s_expected - def test_basic_circuit_not_implemented_ctrl_ops(self): + @pytest.mark.parametrize("wires_map", [wires_dict, None]) + def test_basic_circuit_not_implemented_ctrl_ops(self, wires_map): """Test expected serialization for a simple circuit""" ops = qml.OrbitalRotation(0.1234, wires=range(4)) with qml.tape.QuantumTape() as tape: @@ -444,7 +455,7 @@ def test_basic_circuit_not_implemented_ctrl_ops(self): qml.RY(0.6, wires=1) qml.ctrl(ops, [4, 5]) - s = QuantumScriptSerializer(device_name).serialize_ops(tape, self.wires_dict) + s = QuantumScriptSerializer(device_name).serialize_ops(tape, wires_map) s_expected = ( ( ["RX", "RY", "QubitUnitary"], @@ -464,14 +475,15 @@ def test_basic_circuit_not_implemented_ctrl_ops(self): assert s[0][5] == s_expected[0][5] assert s[1] == s_expected[1] - def test_multicontrolledx(self): + @pytest.mark.parametrize("wires_map", [wires_dict, None]) + def test_multicontrolledx(self, wires_map): """Test expected serialization for a simple circuit""" with qml.tape.QuantumTape() as tape: qml.RX(0.4, wires=0) qml.RY(0.6, wires=1) qml.ctrl(qml.PauliX(wires=0), [1, 2, 3], control_values=[True, False, False]) - s = QuantumScriptSerializer(device_name).serialize_ops(tape, self.wires_dict) + s = QuantumScriptSerializer(device_name).serialize_ops(tape, wires_map) s_expected = ( ( ["RX", "RY", "PauliX"], @@ -486,8 +498,9 @@ def test_multicontrolledx(self): ) assert s == s_expected + @pytest.mark.parametrize("wires_map", [wires_dict, None]) @pytest.mark.parametrize("stateprep", [qml.QubitStateVector, qml.StatePrep]) - def test_skips_prep_circuit(self, stateprep): + def test_skips_prep_circuit(self, stateprep, wires_map): """Test expected serialization for a simple circuit with state preparation, such that the state preparation is skipped""" with qml.tape.QuantumTape() as tape: @@ -497,7 +510,7 @@ def test_skips_prep_circuit(self, stateprep): qml.RY(0.6, wires=1) qml.CNOT(wires=[0, 1]) - s = QuantumScriptSerializer(device_name).serialize_ops(tape, self.wires_dict) + s = QuantumScriptSerializer(device_name).serialize_ops(tape, wires_map) s_expected = ( ( ["RX", "RY", "CNOT"], @@ -512,14 +525,15 @@ def test_skips_prep_circuit(self, stateprep): ) assert s == s_expected - def test_unsupported_kernel_circuit(self): + @pytest.mark.parametrize("wires_map", [wires_dict, None]) + def test_unsupported_kernel_circuit(self, wires_map): """Test expected serialization for a circuit including gates that do not have a dedicated kernel""" with qml.tape.QuantumTape() as tape: qml.CNOT(wires=[0, 1]) qml.RZ(0.2, wires=2) - s = QuantumScriptSerializer(device_name).serialize_ops(tape, self.wires_dict) + s = QuantumScriptSerializer(device_name).serialize_ops(tape, wires_map) s_expected = ( ( ["CNOT", "RZ"], @@ -565,8 +579,9 @@ def test_custom_wires_circuit(self): ) assert s == s_expected + @pytest.mark.parametrize("wires_map", [wires_dict, None]) @pytest.mark.parametrize("C", [True, False]) - def test_integration(self, C): + def test_integration(self, C, wires_map): """Test expected serialization for a random circuit""" with qml.tape.QuantumTape() as tape: qml.RX(0.4, wires=0) @@ -578,7 +593,7 @@ def test_integration(self, C): qml.DoubleExcitationMinus(0.555, wires=[0, 1, 2, 3]) qml.DoubleExcitationPlus(0.555, wires=[0, 1, 2, 3]) - s = QuantumScriptSerializer(device_name).serialize_ops(tape, self.wires_dict) + s = QuantumScriptSerializer(device_name).serialize_ops(tape, wires_map) dtype = np.complex64 if C else np.complex128 s_expected = ( From f80c363db6068e2e9f984fa8858a4537d6f18f75 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Wed, 21 Feb 2024 15:16:47 -0500 Subject: [PATCH 043/428] update measurements with new serialize class --- pennylane_lightning/lightning_qubit/_measurements.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pennylane_lightning/lightning_qubit/_measurements.py b/pennylane_lightning/lightning_qubit/_measurements.py index 3b0fe873f2..2dddd2e5b0 100644 --- a/pennylane_lightning/lightning_qubit/_measurements.py +++ b/pennylane_lightning/lightning_qubit/_measurements.py @@ -33,6 +33,7 @@ from typing import List +from pennylane_lightning.core._serialize import QuantumScriptSerializer from ._serialize import QuantumScriptSerializer from ._state_vector import LightningStateVector From 4f4b29e63cb8ccccc9feda8eee1647967996b4c3 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Wed, 21 Feb 2024 15:22:13 -0500 Subject: [PATCH 044/428] remove outdated test --- tests/lightning_qubit2/test_serialize_2.py | 563 --------------------- 1 file changed, 563 deletions(-) delete mode 100644 tests/lightning_qubit2/test_serialize_2.py diff --git a/tests/lightning_qubit2/test_serialize_2.py b/tests/lightning_qubit2/test_serialize_2.py deleted file mode 100644 index 7f14422c38..0000000000 --- a/tests/lightning_qubit2/test_serialize_2.py +++ /dev/null @@ -1,563 +0,0 @@ -# Copyright 2018-2024 Xanadu Quantum Technologies Inc. - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -""" -Unit tests for the serialization helper functions. -""" -import pytest -from conftest import device_name, LightningDevice - -import numpy as np -import pennylane as qml -from pennylane_lightning.lightning_qubit._serialize import QuantumScriptSerializer - -# from pennylane_lightning.lightning_qubit._serialize_old import QuantumScriptSerializer - -if not LightningDevice._CPP_BINARY_AVAILABLE: - pytest.skip("No binary module found. Skipping.", allow_module_level=True) - -if device_name == "lightning.kokkos": - from pennylane_lightning.lightning_kokkos_ops.observables import ( - NamedObsC64, - NamedObsC128, - HermitianObsC64, - HermitianObsC128, - TensorProdObsC64, - TensorProdObsC128, - HamiltonianC64, - HamiltonianC128, - SparseHamiltonianC64, - SparseHamiltonianC128, - ) -elif device_name == "lightning.gpu": - from pennylane_lightning.lightning_gpu_ops.observables import ( - NamedObsC64, - NamedObsC128, - HermitianObsC64, - HermitianObsC128, - TensorProdObsC64, - TensorProdObsC128, - HamiltonianC64, - HamiltonianC128, - SparseHamiltonianC64, - SparseHamiltonianC128, - ) -else: - from pennylane_lightning.lightning_qubit_ops.observables import ( - NamedObsC64, - NamedObsC128, - HermitianObsC64, - HermitianObsC128, - TensorProdObsC64, - TensorProdObsC128, - HamiltonianC64, - HamiltonianC128, - SparseHamiltonianC64, - SparseHamiltonianC128, - ) - - -def test_wrong_device_name(): - """Test the device name is not a valid option""" - - with pytest.raises(qml.DeviceError, match="The device name"): - QuantumScriptSerializer("thunder.qubit") - - -@pytest.mark.parametrize( - "obs,obs_type", - [ - (qml.PauliZ(0), NamedObsC128), - (qml.PauliZ(0) @ qml.PauliZ(1), TensorProdObsC128), - (qml.Hadamard(0), NamedObsC128), - (qml.Hermitian(np.eye(2), wires=0), HermitianObsC128), - ( - qml.PauliZ(0) @ qml.Hadamard(1) @ (0.1 * (qml.PauliZ(2) + qml.PauliX(3))), - HamiltonianC128, - ), - ( - ( - qml.Hermitian(np.eye(2), wires=0) - @ qml.Hermitian(np.eye(2), wires=1) - @ qml.Projector([0], wires=2) - ), - TensorProdObsC128, - ), - ( - qml.PauliZ(0) @ qml.Hermitian(np.eye(2), wires=1) @ qml.Projector([0], wires=2), - TensorProdObsC128, - ), - (qml.Projector([0], wires=0), HermitianObsC128), - (qml.Hamiltonian([1], [qml.PauliZ(0)]), HamiltonianC128), - (qml.sum(qml.Hadamard(0), qml.PauliX(1)), HermitianObsC128), - ( - qml.SparseHamiltonian(qml.Hamiltonian([1], [qml.PauliZ(0)]).sparse_matrix(), wires=[0]), - SparseHamiltonianC128, - ), - ], -) -def test_obs_returns_expected_type(obs, obs_type): - """Tests that observables get serialized to the expected type.""" - assert isinstance(QuantumScriptSerializer(device_name)._ob(obs), obs_type) - - -class TestSerializeObs: - """Tests for the serialize_observables function""" - - @pytest.mark.parametrize("use_csingle", [True, False]) - def test_tensor_non_tensor_return(self, use_csingle): - """Test expected serialization for a mixture of tensor product and non-tensor product - return""" - with qml.tape.QuantumTape() as tape: - qml.expval(qml.PauliZ(0) @ qml.PauliX(1)) - qml.expval(qml.Hadamard(1)) - - tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 - named_obs = NamedObsC64 if use_csingle else NamedObsC128 - - s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) - - s_expected = [ - tensor_prod_obs([named_obs("PauliZ", [0]), named_obs("PauliX", [1])]), - named_obs("Hadamard", [1]), - ] - - assert s == s_expected - - @pytest.mark.parametrize("use_csingle", [True, False]) - def test_hermitian_return(self, use_csingle): - """Test expected serialization for a Hermitian return""" - with qml.tape.QuantumTape() as tape: - qml.expval(qml.Hermitian(np.eye(4), wires=[0, 1])) - - hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 - c_dtype = np.complex64 if use_csingle else np.complex128 - - s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) - s_expected = hermitian_obs( - np.array( - [1.0, 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, 1.0], - dtype=c_dtype, - ), - [0, 1], - ) - assert s[0] == s_expected - - @pytest.mark.parametrize("use_csingle", [True, False]) - def test_hermitian_tensor_return(self, use_csingle): - """Test expected serialization for a Hermitian return""" - with qml.tape.QuantumTape() as tape: - qml.expval(qml.Hermitian(np.eye(4), wires=[0, 1]) @ qml.Hermitian(np.eye(2), wires=[2])) - - c_dtype = np.complex64 if use_csingle else np.complex128 - tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 - hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 - s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) - - s_expected = tensor_prod_obs( - [ - hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), - hermitian_obs(np.eye(2, dtype=c_dtype).ravel(), [2]), - ] - ) - - assert s[0] == s_expected - - @pytest.mark.parametrize("use_csingle", [True, False]) - def test_mixed_tensor_return(self, use_csingle): - """Test expected serialization for a mixture of Hermitian and Pauli return""" - with qml.tape.QuantumTape() as tape: - qml.expval(qml.Hermitian(np.eye(4), wires=[0, 1]) @ qml.PauliY(2)) - - c_dtype = np.complex64 if use_csingle else np.complex128 - tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 - hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 - named_obs = NamedObsC64 if use_csingle else NamedObsC128 - - s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) - - s_expected = tensor_prod_obs( - [hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), named_obs("PauliY", [2])] - ) - - assert s[0] == s_expected - - @pytest.mark.parametrize("use_csingle", [True, False]) - def test_hamiltonian_return(self, use_csingle): - """Test expected serialization for a Hamiltonian return""" - - ham = qml.Hamiltonian( - [0.3, 0.5, 0.4], - [ - qml.Hermitian(np.eye(4), wires=[0, 1]) @ qml.PauliY(2), - qml.PauliX(0) @ qml.PauliY(2), - qml.Hermitian(np.ones((8, 8)), wires=range(3)), - ], - ) - - with qml.tape.QuantumTape() as tape: - qml.expval(ham) - - hamiltonian_obs = HamiltonianC64 if use_csingle else HamiltonianC128 - named_obs = NamedObsC64 if use_csingle else NamedObsC128 - hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 - tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 - r_dtype = np.float32 if use_csingle else np.float64 - c_dtype = np.complex64 if use_csingle else np.complex128 - - s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) - - s_expected = hamiltonian_obs( - np.array([0.3, 0.5, 0.4], dtype=r_dtype), - [ - tensor_prod_obs( - [ - hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), - named_obs("PauliY", [2]), - ] - ), - tensor_prod_obs([named_obs("PauliX", [0]), named_obs("PauliY", [2])]), - hermitian_obs(np.ones(64, dtype=c_dtype), [0, 1, 2]), - ], - ) - - assert s[0] == s_expected - - @pytest.mark.parametrize("use_csingle", [True, False]) - def test_hamiltonian_tensor_return(self, use_csingle): - """Test expected serialization for a Hamiltonian return""" - - with qml.tape.QuantumTape() as tape: - ham = qml.Hamiltonian( - [0.3, 0.5, 0.4], - [ - qml.Hermitian(np.eye(4), wires=[0, 1]) @ qml.PauliY(2), - qml.PauliX(0) @ qml.PauliY(2), - qml.Hermitian(np.ones((8, 8)), wires=range(3)), - ], - ) - qml.expval(ham @ qml.PauliZ(3)) - - hamiltonian_obs = HamiltonianC64 if use_csingle else HamiltonianC128 - named_obs = NamedObsC64 if use_csingle else NamedObsC128 - hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 - tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 - r_dtype = np.float32 if use_csingle else np.float64 - c_dtype = np.complex64 if use_csingle else np.complex128 - - s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) - - # Expression (ham @ obs) is converted internally by Pennylane - # where obs is appended to each term of the ham - s_expected = hamiltonian_obs( - np.array([0.3, 0.5, 0.4], dtype=r_dtype), - [ - tensor_prod_obs( - [ - hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), - named_obs("PauliY", [2]), - named_obs("PauliZ", [3]), - ] - ), - tensor_prod_obs( - [named_obs("PauliX", [0]), named_obs("PauliY", [2]), named_obs("PauliZ", [3])] - ), - tensor_prod_obs( - [hermitian_obs(np.ones(64, dtype=c_dtype), [0, 1, 2]), named_obs("PauliZ", [3])] - ), - ], - ) - - assert s[0] == s_expected - - @pytest.mark.parametrize("use_csingle", [True, False]) - def test_hamiltonian_mix_return(self, use_csingle): - """Test expected serialization for a Hamiltonian return""" - - ham1 = qml.Hamiltonian( - [0.3, 0.5, 0.4], - [ - qml.Hermitian(np.eye(4), wires=[0, 1]) @ qml.PauliY(2), - qml.PauliX(0) @ qml.PauliY(2), - qml.Hermitian(np.ones((8, 8)), wires=range(3)), - ], - ) - ham2 = qml.Hamiltonian( - [0.7, 0.3], - [qml.PauliX(0) @ qml.Hermitian(np.eye(4), wires=[1, 2]), qml.PauliY(0) @ qml.PauliX(2)], - ) - - with qml.tape.QuantumTape() as tape: - qml.expval(ham1) - qml.expval(ham2) - - hamiltonian_obs = HamiltonianC64 if use_csingle else HamiltonianC128 - named_obs = NamedObsC64 if use_csingle else NamedObsC128 - hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 - tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 - r_dtype = np.float32 if use_csingle else np.float64 - c_dtype = np.complex64 if use_csingle else np.complex128 - - s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) - - s_expected1 = hamiltonian_obs( - np.array([0.3, 0.5, 0.4], dtype=r_dtype), - [ - tensor_prod_obs( - [ - hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), - named_obs("PauliY", [2]), - ] - ), - tensor_prod_obs([named_obs("PauliX", [0]), named_obs("PauliY", [2])]), - hermitian_obs(np.ones(64, dtype=c_dtype), [0, 1, 2]), - ], - ) - s_expected2 = hamiltonian_obs( - np.array([0.7, 0.3], dtype=r_dtype), - [ - tensor_prod_obs( - [ - named_obs("PauliX", [0]), - hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [1, 2]), - ] - ), - tensor_prod_obs([named_obs("PauliY", [0]), named_obs("PauliX", [2])]), - ], - ) - - assert s[0] == s_expected1 - assert s[1] == s_expected2 - - @pytest.mark.parametrize( - "obs,coeffs,terms", - [ - (qml.prod(qml.PauliZ(0), qml.PauliX(1)), [1], [[("PauliX", 1), ("PauliZ", 0)]]), - (qml.s_prod(0.1, qml.PauliX(0)), [0.1], ("PauliX", 0)), - ( - qml.sum( - 0.5 * qml.prod(qml.PauliX(0), qml.PauliZ(1)), - 0.1 * qml.prod(qml.PauliZ(0), qml.PauliY(1)), - ), - [0.5, 0.1], - [[("PauliZ", 1), ("PauliX", 0)], [("PauliY", 1), ("PauliZ", 0)]], - ), - ], - ) - @pytest.mark.parametrize("use_csingle", [True, False]) - def test_op_arithmetic_uses_hamiltonian(self, use_csingle, obs, coeffs, terms): - """Tests that an arithmetic obs with a PauliRep serializes as a Hamiltonian.""" - tape = qml.tape.QuantumTape(measurements=[qml.expval(obs)]) - res, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) - assert len(res) == 1 - assert isinstance(res[0], HamiltonianC64 if use_csingle else HamiltonianC128) - - hamiltonian_obs = HamiltonianC64 if use_csingle else HamiltonianC128 - tensor_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 - named_obs = NamedObsC64 if use_csingle else NamedObsC128 - rtype = np.float32 if use_csingle else np.float64 - term_shape = np.array(terms).shape - - if len(term_shape) == 1: # just a single pauli op - expected_terms = [named_obs(terms[0], [terms[1]])] - elif len(term_shape) == 3: # list of tensor products - expected_terms = [ - tensor_obs([named_obs(pauli, [wire]) for pauli, wire in term]) for term in terms - ] - - coeffs = np.array(coeffs).astype(rtype) - assert res[0] == hamiltonian_obs(coeffs, expected_terms) - - @pytest.mark.parametrize("use_csingle", [True, False]) - def test_multi_wire_identity(self, use_csingle): - """Tests that multi-wire Identity does not fail serialization.""" - tape = qml.tape.QuantumTape(measurements=[qml.expval(qml.Identity(wires=[1, 2]))]) - res, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) - assert len(res) == 1 - - named_obs = NamedObsC64 if use_csingle else NamedObsC128 - assert res[0] == named_obs("Identity", [1]) - - -class TestSerializeOps: - """Tests for the _ops function""" - - def test_basic_circuit(self): - """Test expected serialization for a simple circuit""" - with qml.tape.QuantumTape() as tape: - qml.RX(0.4, wires=0) - qml.RY(0.6, wires=1) - qml.CNOT(wires=[0, 1]) - - s = QuantumScriptSerializer(device_name).serialize_ops(tape) - s_expected = ( - ( - ["RX", "RY", "CNOT"], - [np.array([0.4]), np.array([0.6]), []], - [[0], [1], [0, 1]], - [False, False, False], - [[], [], []], - [[], [], []], - [[], [], []], - ), - False, - ) - assert s == s_expected - - def test_basic_circuit_not_implemented_ctrl_ops(self): - """Test expected serialization for a simple circuit""" - ops = qml.OrbitalRotation(0.1234, wires=range(4)) - with qml.tape.QuantumTape() as tape: - qml.RX(0.4, wires=0) - qml.RY(0.6, wires=1) - qml.ctrl(ops, [4, 5]) - - s = QuantumScriptSerializer(device_name).serialize_ops(tape) - s_expected = ( - ( - ["RX", "RY", "QubitUnitary"], - [np.array([0.4]), np.array([0.6]), [0.0]], - [[0], [1], list(ops.wires)], - [False, False, False], - [[], [], [qml.matrix(ops)]], - [[], [], [4, 5]], - ), - False, - ) - assert s[0][0] == s_expected[0][0] - assert s[0][1] == s_expected[0][1] - assert s[0][2] == s_expected[0][2] - assert s[0][3] == s_expected[0][3] - assert all(np.allclose(s0, s1) for s0, s1 in zip(s[0][4], s_expected[0][4])) - assert s[0][5] == s_expected[0][5] - assert s[1] == s_expected[1] - - def test_multicontrolledx(self): - """Test expected serialization for a simple circuit""" - with qml.tape.QuantumTape() as tape: - qml.RX(0.4, wires=0) - qml.RY(0.6, wires=1) - qml.ctrl(qml.PauliX(wires=0), [1, 2, 3], control_values=[True, False, False]) - - s = QuantumScriptSerializer(device_name).serialize_ops(tape) - s_expected = ( - ( - ["RX", "RY", "PauliX"], - [np.array([0.4]), np.array([0.6]), []], - [[0], [1], [0]], - [False, False, False], - [[], [], []], - [[], [], [1, 2, 3]], - [[], [], [True, False, False]], - ), - False, - ) - assert s == s_expected - - @pytest.mark.parametrize("stateprep", [qml.QubitStateVector, qml.StatePrep]) - def test_skips_prep_circuit(self, stateprep): - """Test expected serialization for a simple circuit with state preparation, such that - the state preparation is skipped""" - with qml.tape.QuantumTape() as tape: - stateprep([1, 0], wires=0) - qml.BasisState([1], wires=1) - qml.RX(0.4, wires=0) - qml.RY(0.6, wires=1) - qml.CNOT(wires=[0, 1]) - - s = QuantumScriptSerializer(device_name).serialize_ops(tape) - s_expected = ( - ( - ["RX", "RY", "CNOT"], - [[0.4], [0.6], []], - [[0], [1], [0, 1]], - [False, False, False], - [[], [], []], - [[], [], []], - [[], [], []], - ), - True, - ) - assert s == s_expected - - def test_unsupported_kernel_circuit(self): - """Test expected serialization for a circuit including gates that do not have a dedicated - kernel""" - with qml.tape.QuantumTape() as tape: - qml.CNOT(wires=[0, 1]) - qml.RZ(0.2, wires=2) - - s = QuantumScriptSerializer(device_name).serialize_ops(tape) - s_expected = ( - ( - ["CNOT", "RZ"], - [[], [0.2]], - [[0, 1], [2]], - [False, False], - ), - False, - ) - assert s[0][0] == s_expected[0][0] - assert s[0][1] == s_expected[0][1] - - @pytest.mark.parametrize("C", [True, False]) - def test_integration(self, C): - """Test expected serialization for a random circuit""" - with qml.tape.QuantumTape() as tape: - qml.RX(0.4, wires=0) - qml.RY(0.6, wires=1) - qml.CNOT(wires=[0, 1]) - qml.QubitUnitary(np.eye(4), wires=[0, 1]) - qml.templates.QFT(wires=[0, 1, 2]) - qml.DoubleExcitation(0.555, wires=[3, 2, 1, 0]) - qml.DoubleExcitationMinus(0.555, wires=[0, 1, 2, 3]) - qml.DoubleExcitationPlus(0.555, wires=[0, 1, 2, 3]) - - s = QuantumScriptSerializer(device_name).serialize_ops(tape) - - dtype = np.complex64 if C else np.complex128 - s_expected = ( - ( - [ - "RX", - "RY", - "CNOT", - "QubitUnitary", - "QFT", - "DoubleExcitation", - "DoubleExcitationMinus", - "DoubleExcitationPlus", - ], - [[0.4], [0.6], [], [0.0], [], [0.555], [0.555], [0.555]], - [[0], [1], [0, 1], [0, 1], [0, 1, 2], [3, 2, 1, 0], [0, 1, 2, 3], [0, 1, 2, 3]], - [False, False, False, False, False, False, False, False], - [ - [], - [], - [], - qml.matrix(qml.QubitUnitary(np.eye(4, dtype=dtype), wires=[0, 1])), - qml.matrix(qml.templates.QFT(wires=[0, 1, 2])), - [], - [], - [], - ], - ), - False, - ) - assert s[0][0] == s_expected[0][0] - assert s[0][1] == s_expected[0][1] - assert s[0][2] == s_expected[0][2] - assert s[0][3] == s_expected[0][3] - assert s[1] == s_expected[1] - - assert all(np.allclose(s1, s2) for s1, s2 in zip(s[0][4], s_expected[0][4])) From 341d47cbfc06360dd8d040dfcc917206ba481a13 Mon Sep 17 00:00:00 2001 From: albi3ro Date: Wed, 21 Feb 2024 15:36:39 -0500 Subject: [PATCH 045/428] register with setup.py, state vector fixes --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 8c6fbfdf68..df732bdf87 100644 --- a/setup.py +++ b/setup.py @@ -186,6 +186,7 @@ def build_extension(self, ext: CMakeExtension): suffix = suffix[0].upper() + suffix[1:] pennylane_plugins = [device_name + " = pennylane_lightning." + backend + ":Lightning" + suffix] +pennylane_plugins.append("lightning.qubit2 = pennylane_lightning.lightning_qubit.lightning_qubit2:LightningQubit2") pkg_suffix = "" if suffix == "Qubit" else "_" + suffix From 1f78bef726ad227a039493a316991e6af27c9da8 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Thu, 22 Feb 2024 07:53:37 -0500 Subject: [PATCH 046/428] remove obsolete tests --- .github/workflows/tests_linux.yml | 47 ------------------------------- tests/conftest.py | 8 +++--- 2 files changed, 4 insertions(+), 51 deletions(-) diff --git a/.github/workflows/tests_linux.yml b/.github/workflows/tests_linux.yml index 1e521ec5bb..91bd44bb85 100644 --- a/.github/workflows/tests_linux.yml +++ b/.github/workflows/tests_linux.yml @@ -197,53 +197,6 @@ jobs: ./main/.coverage-${{ github.job }}-${{ matrix.pl_backend }} if-no-files-found: error - pythontests_LQ_2: - strategy: - matrix: - os: [ubuntu-22.04] - pl_backend: ["lightning_qubit2"] - timeout-minutes: 30 - name: Python tests - runs-on: ${{ matrix.os }} - - steps: - - name: Checkout PennyLane-Lightning - uses: actions/checkout@v3 - with: - fetch-tags: true - path: main - - - uses: actions/setup-python@v4 - name: Install Python - with: - python-version: '3.9' - - - name: Install dependencies - run: sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION - - - name: Get required Python packages - run: | - cd main - python -m pip install -r requirements-dev.txt - python -m pip install openfermionpyscf - - - name: Checkout PennyLane for release build - uses: actions/checkout@v3 - with: - path: pennylane - repository: PennyLaneAI/pennylane - - - name: Install backend device - run: | - cd main - PL_BACKEND=lightning.qubit pip install -e . -vv - - - name: Run PennyLane-Lightning unit tests - run: | - cd main/ - DEVICENAME=`echo ${{ matrix.pl_backend }} | sed "s/_/./g"` - PL_DEVICE=${DEVICENAME} python -m pytest tests/lightning_qubit2/ - cpptestswithOpenBLAS: if: ${{ !contains(fromJSON('["schedule", "workflow_dispatch"]'), github.event_name) }} strategy: diff --git a/tests/conftest.py b/tests/conftest.py index d04117520c..e10a3c203c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -113,10 +113,10 @@ def get_device(): device_name = get_device() -# if device_name not in qml.plugin_devices: -# raise qml.DeviceError( -# f"Device {device_name} does not exist. Make sure the required plugin is installed." -# ) +if device_name not in qml.plugin_devices: + raise qml.DeviceError( + f"Device {device_name} does not exist. Make sure the required plugin is installed." + ) # Device specification import pennylane_lightning.lightning_qubit as lightning_ops # Any definition of lightning_ops will do From b7ccb84a634c260d8dad0a71b779097de6761cf2 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Thu, 22 Feb 2024 08:04:18 -0500 Subject: [PATCH 047/428] remove unused dtype input from simulate --- pennylane_lightning/lightning_qubit/lightning_qubit2.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index fb28c8cfbd..c5bf43531d 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -36,14 +36,12 @@ LQ_CPP_BINARY_AVAILABLE = False -def simulate(circuit: QuantumScript, state: LightningStateVector, dtype=np.complex128) -> Result: +def simulate(circuit: QuantumScript, state: LightningStateVector) -> Result: """Simulate a single quantum script. Args: circuit (QuantumTape): The single circuit to simulate state (LightningStateVector): handle to Lightning state vector - dtype: Datatypes for state-vector representation. Must be one of - ``np.complex64`` or ``np.complex128``. Returns: tuple(TensorLike): The results of the simulation From 58032ef23fc2d008fd3040f4d6b4890e9ec1f1d7 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Thu, 22 Feb 2024 08:14:11 -0500 Subject: [PATCH 048/428] update measurements --- pennylane_lightning/lightning_qubit/_measurements.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/_measurements.py b/pennylane_lightning/lightning_qubit/_measurements.py index 2dddd2e5b0..3feba583ef 100644 --- a/pennylane_lightning/lightning_qubit/_measurements.py +++ b/pennylane_lightning/lightning_qubit/_measurements.py @@ -23,18 +23,15 @@ except ImportError: pass -import numpy as np from typing import Callable, List +import numpy as np from pennylane.measurements import StateMeasurement, MeasurementProcess, ExpectationMP from pennylane.typing import TensorLike, Result from pennylane.tape import QuantumScript from pennylane.wires import Wires -from typing import List - from pennylane_lightning.core._serialize import QuantumScriptSerializer -from ._serialize import QuantumScriptSerializer from ._state_vector import LightningStateVector @@ -80,6 +77,7 @@ def state_diagonalizing_gates(self, measurementprocess: StateMeasurement) -> Ten wires = Wires(range(total_wires)) return measurementprocess.process_state(state_array, wires) + # pylint: disable=protected-access def expval(self, measurementprocess: MeasurementProcess): """Expectation value of the supplied observable contained in the MeasurementProcess. From a3b696e5c09b9782c891b1134ad055070294e1db Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Thu, 22 Feb 2024 08:14:27 -0500 Subject: [PATCH 049/428] update state_vector --- pennylane_lightning/lightning_qubit/_state_vector.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/_state_vector.py b/pennylane_lightning/lightning_qubit/_state_vector.py index 8feeae0727..b116c5fa93 100644 --- a/pennylane_lightning/lightning_qubit/_state_vector.py +++ b/pennylane_lightning/lightning_qubit/_state_vector.py @@ -26,6 +26,7 @@ except ImportError: pass +from itertools import product import numpy as np import pennylane as qml @@ -34,12 +35,8 @@ from pennylane import ( BasisState, StatePrep, - DeviceError, ) -from itertools import product - - class LightningStateVector: """Lightning state-vector class. @@ -208,11 +205,11 @@ def _get_basis_state_index(self, state, wires): if not set(state.tolist()).issubset({0, 1}): raise ValueError("BasisState parameter must consist of 0 or 1 integers.") - if n_basis_state != len(device_wires): + if n_basis_state != len(wires): raise ValueError("BasisState parameter and wires must be of equal length.") # get computational basis state number - basis_states = 2 ** (self.num_wires - 1 - np.array(device_wires)) + basis_states = 2 ** (self.num_wires - 1 - np.array(wires)) basis_states = qml.math.convert_like(basis_states, state) return int(qml.math.dot(state, basis_states)) From cd7c67cbe0c8b165ede5f31cc2731c97c1cb734b Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Thu, 22 Feb 2024 08:14:47 -0500 Subject: [PATCH 050/428] update lightning_qubit2 --- pennylane_lightning/lightning_qubit/lightning_qubit2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index c5bf43531d..b35b513391 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -25,7 +25,7 @@ try: - # pylint: disable=import-error, no-name-in-module + # pylint: disable=import-error, no-name-in-module, unused-import from pennylane_lightning.lightning_qubit_ops import ( StateVectorC64, StateVectorC128, From 910246b644251c43fe4930e3195228856acac913 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Thu, 22 Feb 2024 08:20:10 -0500 Subject: [PATCH 051/428] format --- pennylane_lightning/lightning_qubit/_state_vector.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pennylane_lightning/lightning_qubit/_state_vector.py b/pennylane_lightning/lightning_qubit/_state_vector.py index b116c5fa93..6a574bec20 100644 --- a/pennylane_lightning/lightning_qubit/_state_vector.py +++ b/pennylane_lightning/lightning_qubit/_state_vector.py @@ -37,6 +37,7 @@ StatePrep, ) + class LightningStateVector: """Lightning state-vector class. From af6cfefd4a9bfebfbc4a30cf0fe94624295c1790 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Thu, 22 Feb 2024 08:25:47 -0500 Subject: [PATCH 052/428] pylint --- pennylane_lightning/lightning_qubit/_measurements.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pennylane_lightning/lightning_qubit/_measurements.py b/pennylane_lightning/lightning_qubit/_measurements.py index 3feba583ef..120a6696bb 100644 --- a/pennylane_lightning/lightning_qubit/_measurements.py +++ b/pennylane_lightning/lightning_qubit/_measurements.py @@ -15,6 +15,7 @@ Class implementation for state vector measurements. """ +# pylint: disable=import-error, no-name-in-module, ungrouped-imports try: from pennylane_lightning.lightning_qubit_ops import ( MeasurementsC64, From dd31965325141035de765c28cff784a420656420 Mon Sep 17 00:00:00 2001 From: Amintor Dusko <87949283+AmintorDusko@users.noreply.github.com> Date: Thu, 22 Feb 2024 08:27:48 -0500 Subject: [PATCH 053/428] Update pennylane_lightning/lightning_qubit/_state_vector.py Co-authored-by: Christina Lee --- pennylane_lightning/lightning_qubit/_state_vector.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/lightning_qubit/_state_vector.py b/pennylane_lightning/lightning_qubit/_state_vector.py index 6a574bec20..0fa7f24ec8 100644 --- a/pennylane_lightning/lightning_qubit/_state_vector.py +++ b/pennylane_lightning/lightning_qubit/_state_vector.py @@ -234,7 +234,7 @@ def _apply_state_vector(self, state, device_wires): if len(device_wires) == self.num_wires and Wires(sorted(device_wires)) == device_wires: # Initialize the entire device state with the input state - state = self._reshape(state, output_shape).ravel(order="C") + state = np.reshape(state, output_shape).ravel(order="C") self._qubit_state.UpdateData(state) return From 44bc987b1b6b7b2b592f369332fe509b5a5502f2 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Thu, 22 Feb 2024 09:29:57 -0500 Subject: [PATCH 054/428] remove old comment --- .../lightning_qubit/_state_vector.py | 2 - tests/lightning_qubit2/test_state_vector.py | 658 ++++++++++++++++++ tests/test_serialize.py | 2 +- 3 files changed, 659 insertions(+), 3 deletions(-) create mode 100644 tests/lightning_qubit2/test_state_vector.py diff --git a/pennylane_lightning/lightning_qubit/_state_vector.py b/pennylane_lightning/lightning_qubit/_state_vector.py index 0fa7f24ec8..5d82aa6b34 100644 --- a/pennylane_lightning/lightning_qubit/_state_vector.py +++ b/pennylane_lightning/lightning_qubit/_state_vector.py @@ -198,8 +198,6 @@ def _get_basis_state_index(self, state, wires): Returns: int: basis state index """ - # translate to wire labels used by device - # length of basis state parameter n_basis_state = len(state) diff --git a/tests/lightning_qubit2/test_state_vector.py b/tests/lightning_qubit2/test_state_vector.py new file mode 100644 index 0000000000..e81f5c1783 --- /dev/null +++ b/tests/lightning_qubit2/test_state_vector.py @@ -0,0 +1,658 @@ +# Copyright 2018-2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +Unit tests for the serialization helper functions. +""" +import pytest +from conftest import device_name, LightningDevice + +import numpy as np +import pennylane as qml +from pennylane_lightning.core._serialize import QuantumScriptSerializer, global_phase_diagonal + +if not LightningDevice._CPP_BINARY_AVAILABLE: + pytest.skip("No binary module found. Skipping.", allow_module_level=True) + +if device_name == "lightning.kokkos": + from pennylane_lightning.lightning_kokkos_ops.observables import ( + NamedObsC64, + NamedObsC128, + HermitianObsC64, + HermitianObsC128, + TensorProdObsC64, + TensorProdObsC128, + HamiltonianC64, + HamiltonianC128, + SparseHamiltonianC64, + SparseHamiltonianC128, + ) +elif device_name == "lightning.gpu": + from pennylane_lightning.lightning_gpu_ops.observables import ( + NamedObsC64, + NamedObsC128, + HermitianObsC64, + HermitianObsC128, + TensorProdObsC64, + TensorProdObsC128, + HamiltonianC64, + HamiltonianC128, + SparseHamiltonianC64, + SparseHamiltonianC128, + ) +else: + from pennylane_lightning.lightning_qubit_ops.observables import ( + NamedObsC64, + NamedObsC128, + HermitianObsC64, + HermitianObsC128, + TensorProdObsC64, + TensorProdObsC128, + HamiltonianC64, + HamiltonianC128, + SparseHamiltonianC64, + SparseHamiltonianC128, + ) + + +def test_wrong_device_name(): + """Test the device name is not a valid option""" + + with pytest.raises(qml.DeviceError, match="The device name"): + QuantumScriptSerializer("thunder.qubit") + + +@pytest.mark.parametrize( + "obs,obs_type", + [ + (qml.PauliZ(0), NamedObsC128), + (qml.PauliZ(0) @ qml.PauliZ(1), TensorProdObsC128), + (qml.Hadamard(0), NamedObsC128), + (qml.Hermitian(np.eye(2), wires=0), HermitianObsC128), + ( + qml.PauliZ(0) @ qml.Hadamard(1) @ (0.1 * (qml.PauliZ(2) + qml.PauliX(3))), + HamiltonianC128, + ), + ( + ( + qml.Hermitian(np.eye(2), wires=0) + @ qml.Hermitian(np.eye(2), wires=1) + @ qml.Projector([0], wires=2) + ), + TensorProdObsC128, + ), + ( + qml.PauliZ(0) @ qml.Hermitian(np.eye(2), wires=1) @ qml.Projector([0], wires=2), + TensorProdObsC128, + ), + (qml.Projector([0], wires=0), HermitianObsC128), + (qml.Hamiltonian([1], [qml.PauliZ(0)]), HamiltonianC128), + (qml.sum(qml.Hadamard(0), qml.PauliX(1)), HermitianObsC128), + ( + qml.SparseHamiltonian(qml.Hamiltonian([1], [qml.PauliZ(0)]).sparse_matrix(), wires=[0]), + SparseHamiltonianC128, + ), + ], +) +def test_obs_returns_expected_type(obs, obs_type): + """Tests that observables get serialized to the expected type, with and without wires map""" + assert isinstance( + QuantumScriptSerializer(device_name)._ob(obs, dict(enumerate(obs.wires))), obs_type + ) + assert isinstance(QuantumScriptSerializer(device_name)._ob(obs), obs_type) + + +class TestSerializeObs: + """Tests for the _observables function""" + + wires_dict = {i: i for i in range(10)} + + @pytest.mark.parametrize("use_csingle", [True, False]) + @pytest.mark.parametrize("wires_map", [wires_dict, None]) + def test_tensor_non_tensor_return(self, use_csingle, wires_map): + """Test expected serialization for a mixture of tensor product and non-tensor product + return""" + with qml.tape.QuantumTape() as tape: + qml.expval(qml.PauliZ(0) @ qml.PauliX(1)) + qml.expval(qml.Hadamard(1)) + + tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 + named_obs = NamedObsC64 if use_csingle else NamedObsC128 + + s_expected = [ + tensor_prod_obs([named_obs("PauliZ", [0]), named_obs("PauliX", [1])]), + named_obs("Hadamard", [1]), + ] + + s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables( + tape, wires_map + ) + assert s == s_expected + + @pytest.mark.parametrize("use_csingle", [True, False]) + @pytest.mark.parametrize("wires_map", [wires_dict, None]) + def test_hermitian_return(self, use_csingle, wires_map): + """Test expected serialization for a Hermitian return""" + with qml.tape.QuantumTape() as tape: + qml.expval(qml.Hermitian(np.eye(4), wires=[0, 1])) + + hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 + c_dtype = np.complex64 if use_csingle else np.complex128 + + s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables( + tape, wires_map + ) + s_expected = hermitian_obs( + np.array( + [1.0, 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, 1.0], + dtype=c_dtype, + ), + [0, 1], + ) + assert s[0] == s_expected + + @pytest.mark.parametrize("use_csingle", [True, False]) + @pytest.mark.parametrize("wires_map", [wires_dict, None]) + def test_hermitian_tensor_return(self, use_csingle, wires_map): + """Test expected serialization for a Hermitian return""" + with qml.tape.QuantumTape() as tape: + qml.expval(qml.Hermitian(np.eye(4), wires=[0, 1]) @ qml.Hermitian(np.eye(2), wires=[2])) + + c_dtype = np.complex64 if use_csingle else np.complex128 + tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 + hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 + s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables( + tape, wires_map + ) + + s_expected = tensor_prod_obs( + [ + hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), + hermitian_obs(np.eye(2, dtype=c_dtype).ravel(), [2]), + ] + ) + + assert s[0] == s_expected + + @pytest.mark.parametrize("use_csingle", [True, False]) + @pytest.mark.parametrize("wires_map", [wires_dict, None]) + def test_mixed_tensor_return(self, use_csingle, wires_map): + """Test expected serialization for a mixture of Hermitian and Pauli return""" + with qml.tape.QuantumTape() as tape: + qml.expval(qml.Hermitian(np.eye(4), wires=[0, 1]) @ qml.PauliY(2)) + + c_dtype = np.complex64 if use_csingle else np.complex128 + tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 + hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 + named_obs = NamedObsC64 if use_csingle else NamedObsC128 + + s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables( + tape, wires_map + ) + + s_expected = tensor_prod_obs( + [hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), named_obs("PauliY", [2])] + ) + + assert s[0] == s_expected + + @pytest.mark.parametrize("use_csingle", [True, False]) + @pytest.mark.parametrize("wires_map", [wires_dict, None]) + def test_hamiltonian_return(self, use_csingle, wires_map): + """Test expected serialization for a Hamiltonian return""" + + ham = qml.Hamiltonian( + [0.3, 0.5, 0.4], + [ + qml.Hermitian(np.eye(4), wires=[0, 1]) @ qml.PauliY(2), + qml.PauliX(0) @ qml.PauliY(2), + qml.Hermitian(np.ones((8, 8)), wires=range(3)), + ], + ) + + with qml.tape.QuantumTape() as tape: + qml.expval(ham) + + hamiltonian_obs = HamiltonianC64 if use_csingle else HamiltonianC128 + named_obs = NamedObsC64 if use_csingle else NamedObsC128 + hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 + tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 + r_dtype = np.float32 if use_csingle else np.float64 + c_dtype = np.complex64 if use_csingle else np.complex128 + + s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables( + tape, wires_map + ) + + s_expected = hamiltonian_obs( + np.array([0.3, 0.5, 0.4], dtype=r_dtype), + [ + tensor_prod_obs( + [ + hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), + named_obs("PauliY", [2]), + ] + ), + tensor_prod_obs([named_obs("PauliX", [0]), named_obs("PauliY", [2])]), + hermitian_obs(np.ones(64, dtype=c_dtype), [0, 1, 2]), + ], + ) + + assert s[0] == s_expected + + @pytest.mark.parametrize("use_csingle", [True, False]) + @pytest.mark.parametrize("wires_map", [wires_dict, None]) + def test_hamiltonian_tensor_return(self, use_csingle, wires_map): + """Test expected serialization for a Hamiltonian return""" + + with qml.tape.QuantumTape() as tape: + ham = qml.Hamiltonian( + [0.3, 0.5, 0.4], + [ + qml.Hermitian(np.eye(4), wires=[0, 1]) @ qml.PauliY(2), + qml.PauliX(0) @ qml.PauliY(2), + qml.Hermitian(np.ones((8, 8)), wires=range(3)), + ], + ) + qml.expval(ham @ qml.PauliZ(3)) + + hamiltonian_obs = HamiltonianC64 if use_csingle else HamiltonianC128 + named_obs = NamedObsC64 if use_csingle else NamedObsC128 + hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 + tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 + r_dtype = np.float32 if use_csingle else np.float64 + c_dtype = np.complex64 if use_csingle else np.complex128 + + s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables( + tape, wires_map + ) + + # Expression (ham @ obs) is converted internally by Pennylane + # where obs is appended to each term of the ham + s_expected = hamiltonian_obs( + np.array([0.3, 0.5, 0.4], dtype=r_dtype), + [ + tensor_prod_obs( + [ + hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), + named_obs("PauliY", [2]), + named_obs("PauliZ", [3]), + ] + ), + tensor_prod_obs( + [named_obs("PauliX", [0]), named_obs("PauliY", [2]), named_obs("PauliZ", [3])] + ), + tensor_prod_obs( + [hermitian_obs(np.ones(64, dtype=c_dtype), [0, 1, 2]), named_obs("PauliZ", [3])] + ), + ], + ) + + assert s[0] == s_expected + + @pytest.mark.parametrize("use_csingle", [True, False]) + @pytest.mark.parametrize("wires_map", [wires_dict, None]) + def test_hamiltonian_mix_return(self, use_csingle, wires_map): + """Test expected serialization for a Hamiltonian return""" + + ham1 = qml.Hamiltonian( + [0.3, 0.5, 0.4], + [ + qml.Hermitian(np.eye(4), wires=[0, 1]) @ qml.PauliY(2), + qml.PauliX(0) @ qml.PauliY(2), + qml.Hermitian(np.ones((8, 8)), wires=range(3)), + ], + ) + ham2 = qml.Hamiltonian( + [0.7, 0.3], + [qml.PauliX(0) @ qml.Hermitian(np.eye(4), wires=[1, 2]), qml.PauliY(0) @ qml.PauliX(2)], + ) + + with qml.tape.QuantumTape() as tape: + qml.expval(ham1) + qml.expval(ham2) + + hamiltonian_obs = HamiltonianC64 if use_csingle else HamiltonianC128 + named_obs = NamedObsC64 if use_csingle else NamedObsC128 + hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 + tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 + r_dtype = np.float32 if use_csingle else np.float64 + c_dtype = np.complex64 if use_csingle else np.complex128 + + s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables( + tape, wires_map + ) + + s_expected1 = hamiltonian_obs( + np.array([0.3, 0.5, 0.4], dtype=r_dtype), + [ + tensor_prod_obs( + [ + hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), + named_obs("PauliY", [2]), + ] + ), + tensor_prod_obs([named_obs("PauliX", [0]), named_obs("PauliY", [2])]), + hermitian_obs(np.ones(64, dtype=c_dtype), [0, 1, 2]), + ], + ) + s_expected2 = hamiltonian_obs( + np.array([0.7, 0.3], dtype=r_dtype), + [ + tensor_prod_obs( + [ + named_obs("PauliX", [0]), + hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [1, 2]), + ] + ), + tensor_prod_obs([named_obs("PauliY", [0]), named_obs("PauliX", [2])]), + ], + ) + + assert s[0] == s_expected1 + assert s[1] == s_expected2 + + @pytest.mark.parametrize( + "obs,coeffs,terms", + [ + (qml.prod(qml.PauliZ(0), qml.PauliX(1)), [1], [[("PauliX", 1), ("PauliZ", 0)]]), + (qml.s_prod(0.1, qml.PauliX(0)), [0.1], ("PauliX", 0)), + ( + qml.sum( + 0.5 * qml.prod(qml.PauliX(0), qml.PauliZ(1)), + 0.1 * qml.prod(qml.PauliZ(0), qml.PauliY(1)), + ), + [0.5, 0.1], + [[("PauliZ", 1), ("PauliX", 0)], [("PauliY", 1), ("PauliZ", 0)]], + ), + ], + ) + @pytest.mark.parametrize("use_csingle", [True, False]) + @pytest.mark.parametrize("wires_map", [wires_dict, None]) + def test_op_arithmetic_uses_hamiltonian(self, use_csingle, obs, coeffs, terms, wires_map): + """Tests that an arithmetic obs with a PauliRep serializes as a Hamiltonian.""" + tape = qml.tape.QuantumTape(measurements=[qml.expval(obs)]) + res, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables( + tape, wires_map + ) + assert len(res) == 1 + assert isinstance(res[0], HamiltonianC64 if use_csingle else HamiltonianC128) + + hamiltonian_obs = HamiltonianC64 if use_csingle else HamiltonianC128 + tensor_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 + named_obs = NamedObsC64 if use_csingle else NamedObsC128 + rtype = np.float32 if use_csingle else np.float64 + term_shape = np.array(terms).shape + + if len(term_shape) == 1: # just a single pauli op + expected_terms = [named_obs(terms[0], [terms[1]])] + elif len(term_shape) == 3: # list of tensor products + expected_terms = [ + tensor_obs([named_obs(pauli, [wire]) for pauli, wire in term]) for term in terms + ] + + coeffs = np.array(coeffs).astype(rtype) + assert res[0] == hamiltonian_obs(coeffs, expected_terms) + + @pytest.mark.parametrize("use_csingle", [True, False]) + @pytest.mark.parametrize("wires_map", [wires_dict, None]) + def test_multi_wire_identity(self, use_csingle, wires_map): + """Tests that multi-wire Identity does not fail serialization.""" + tape = qml.tape.QuantumTape(measurements=[qml.expval(qml.Identity(wires=[1, 2]))]) + res, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables( + tape, wires_map + ) + assert len(res) == 1 + + named_obs = NamedObsC64 if use_csingle else NamedObsC128 + assert res[0] == named_obs("Identity", [1]) + + +class TestSerializeOps: + """Tests for the _ops function""" + + wires_dict = {i: i for i in range(10)} + + @pytest.mark.parametrize("wires_map", [wires_dict, None]) + def test_basic_circuit(self, wires_map): + """Test expected serialization for a simple circuit""" + with qml.tape.QuantumTape() as tape: + qml.RX(0.4, wires=0) + qml.RY(0.6, wires=1) + qml.CNOT(wires=[0, 1]) + + s = QuantumScriptSerializer(device_name).serialize_ops(tape, wires_map) + s_expected = ( + ( + ["RX", "RY", "CNOT"], + [np.array([0.4]), np.array([0.6]), []], + [[0], [1], [0, 1]], + [False, False, False], + [[], [], []], + [[], [], []], + [[], [], []], + ), + False, + ) + assert s == s_expected + + @pytest.mark.parametrize("wires_map", [wires_dict, None]) + def test_basic_circuit_not_implemented_ctrl_ops(self, wires_map): + """Test expected serialization for a simple circuit""" + ops = qml.OrbitalRotation(0.1234, wires=range(4)) + with qml.tape.QuantumTape() as tape: + qml.RX(0.4, wires=0) + qml.RY(0.6, wires=1) + qml.ctrl(ops, [4, 5]) + + s = QuantumScriptSerializer(device_name).serialize_ops(tape, wires_map) + s_expected = ( + ( + ["RX", "RY", "QubitUnitary"], + [np.array([0.4]), np.array([0.6]), [0.0]], + [[0], [1], list(ops.wires)], + [False, False, False], + [[], [], [qml.matrix(ops)]], + [[], [], [4, 5]], + ), + False, + ) + assert s[0][0] == s_expected[0][0] + assert s[0][1] == s_expected[0][1] + assert s[0][2] == s_expected[0][2] + assert s[0][3] == s_expected[0][3] + assert all(np.allclose(s0, s1) for s0, s1 in zip(s[0][4], s_expected[0][4])) + assert s[0][5] == s_expected[0][5] + assert s[1] == s_expected[1] + + @pytest.mark.parametrize("wires_map", [wires_dict, None]) + def test_multicontrolledx(self, wires_map): + """Test expected serialization for a simple circuit""" + with qml.tape.QuantumTape() as tape: + qml.RX(0.4, wires=0) + qml.RY(0.6, wires=1) + qml.ctrl(qml.PauliX(wires=0), [1, 2, 3], control_values=[True, False, False]) + + s = QuantumScriptSerializer(device_name).serialize_ops(tape, wires_map) + s_expected = ( + ( + ["RX", "RY", "PauliX"], + [np.array([0.4]), np.array([0.6]), []], + [[0], [1], [0]], + [False, False, False], + [[], [], []], + [[], [], [1, 2, 3]], + [[], [], [True, False, False]], + ), + False, + ) + assert s == s_expected + + @pytest.mark.parametrize("wires_map", [wires_dict, None]) + @pytest.mark.parametrize("stateprep", [qml.QubitStateVector, qml.StatePrep]) + def test_skips_prep_circuit(self, stateprep, wires_map): + """Test expected serialization for a simple circuit with state preparation, such that + the state preparation is skipped""" + with qml.tape.QuantumTape() as tape: + stateprep([1, 0], wires=0) + qml.BasisState([1], wires=1) + qml.RX(0.4, wires=0) + qml.RY(0.6, wires=1) + qml.CNOT(wires=[0, 1]) + + s = QuantumScriptSerializer(device_name).serialize_ops(tape, wires_map) + s_expected = ( + ( + ["RX", "RY", "CNOT"], + [[0.4], [0.6], []], + [[0], [1], [0, 1]], + [False, False, False], + [[], [], []], + [[], [], []], + [[], [], []], + ), + True, + ) + assert s == s_expected + + @pytest.mark.parametrize("wires_map", [wires_dict, None]) + def test_unsupported_kernel_circuit(self, wires_map): + """Test expected serialization for a circuit including gates that do not have a dedicated + kernel""" + with qml.tape.QuantumTape() as tape: + qml.CNOT(wires=[0, 1]) + qml.RZ(0.2, wires=2) + + s = QuantumScriptSerializer(device_name).serialize_ops(tape, wires_map) + s_expected = ( + ( + ["CNOT", "RZ"], + [[], [0.2]], + [[0, 1], [2]], + [False, False], + ), + False, + ) + assert s[0][0] == s_expected[0][0] + assert s[0][1] == s_expected[0][1] + + def test_custom_wires_circuit(self): + """Test expected serialization for a simple circuit with custom wire labels""" + wires_dict = {"a": 0, 3.2: 1} + with qml.tape.QuantumTape() as tape: + qml.RX(0.4, wires="a") + qml.RY(0.6, wires=3.2) + qml.CNOT(wires=["a", 3.2]) + qml.SingleExcitation(0.5, wires=["a", 3.2]) + qml.SingleExcitationPlus(0.4, wires=["a", 3.2]) + qml.adjoint(qml.SingleExcitationMinus(0.5, wires=["a", 3.2]), lazy=False) + + s = QuantumScriptSerializer(device_name).serialize_ops(tape, wires_dict) + s_expected = ( + ( + [ + "RX", + "RY", + "CNOT", + "SingleExcitation", + "SingleExcitationPlus", + "SingleExcitationMinus", + ], + [[0.4], [0.6], [], [0.5], [0.4], [-0.5]], + [[0], [1], [0, 1], [0, 1], [0, 1], [0, 1]], + [False, False, False, False, False, False], + [[], [], [], [], [], []], + [[], [], [], [], [], []], + [[], [], [], [], [], []], + ), + False, + ) + assert s == s_expected + + @pytest.mark.parametrize("wires_map", [wires_dict, None]) + @pytest.mark.parametrize("C", [True, False]) + def test_integration(self, C, wires_map): + """Test expected serialization for a random circuit""" + with qml.tape.QuantumTape() as tape: + qml.RX(0.4, wires=0) + qml.RY(0.6, wires=1) + qml.CNOT(wires=[0, 1]) + qml.QubitUnitary(np.eye(4), wires=[0, 1]) + qml.templates.QFT(wires=[0, 1, 2]) + qml.DoubleExcitation(0.555, wires=[3, 2, 1, 0]) + qml.DoubleExcitationMinus(0.555, wires=[0, 1, 2, 3]) + qml.DoubleExcitationPlus(0.555, wires=[0, 1, 2, 3]) + + s = QuantumScriptSerializer(device_name).serialize_ops(tape, wires_map) + + dtype = np.complex64 if C else np.complex128 + s_expected = ( + ( + [ + "RX", + "RY", + "CNOT", + "QubitUnitary", + "QFT", + "DoubleExcitation", + "DoubleExcitationMinus", + "DoubleExcitationPlus", + ], + [[0.4], [0.6], [], [0.0], [], [0.555], [0.555], [0.555]], + [[0], [1], [0, 1], [0, 1], [0, 1, 2], [3, 2, 1, 0], [0, 1, 2, 3], [0, 1, 2, 3]], + [False, False, False, False, False, False, False, False], + [ + [], + [], + [], + qml.matrix(qml.QubitUnitary(np.eye(4, dtype=dtype), wires=[0, 1])), + qml.matrix(qml.templates.QFT(wires=[0, 1, 2])), + [], + [], + [], + ], + ), + False, + ) + assert s[0][0] == s_expected[0][0] + assert s[0][1] == s_expected[0][1] + assert s[0][2] == s_expected[0][2] + assert s[0][3] == s_expected[0][3] + assert s[1] == s_expected[1] + + assert all(np.allclose(s1, s2) for s1, s2 in zip(s[0][4], s_expected[0][4])) + + +def check_global_phase_diagonal(par, wires, targets, controls, control_values): + op = qml.ctrl(qml.GlobalPhase(par, wires=targets), controls, control_values=control_values) + return np.diag(op.matrix(wires)) + + +def test_global_phase(): + """Validate global_phase_diagonal with various combinations of num_qubits, targets and controls.""" + import itertools + + nmax = 7 + par = 0.1 + for nq in range(2, nmax): + wires = range(nq) + for nw in range(nq, nmax): + wire_lists = list(itertools.permutations(wires, nw)) + for wire_list in wire_lists: + for i in range(len(wire_list) - 1): + targets = wire_list[0:i] + controls = wire_list[i:] + control_values = [i % 2 == 0 for i in controls] + D0 = check_global_phase_diagonal(par, wires, targets, controls, control_values) + D1 = global_phase_diagonal(par, wires, controls, control_values) + assert np.allclose(D0, D1) diff --git a/tests/test_serialize.py b/tests/test_serialize.py index 01ba41dbbd..e81f5c1783 100644 --- a/tests/test_serialize.py +++ b/tests/test_serialize.py @@ -1,4 +1,4 @@ -# Copyright 2018-2023 Xanadu Quantum Technologies Inc. +# Copyright 2018-2024 Xanadu Quantum Technologies Inc. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. From 5a5b20a5f87532eac9deae0465a851c76caa83c9 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Thu, 22 Feb 2024 09:50:52 -0500 Subject: [PATCH 055/428] some review suggestions --- pennylane_lightning/lightning_qubit/lightning_qubit2.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index b35b513391..bbdc799400 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -25,11 +25,8 @@ try: - # pylint: disable=import-error, no-name-in-module, unused-import - from pennylane_lightning.lightning_qubit_ops import ( - StateVectorC64, - StateVectorC128, - ) + # pylint: disable=import-error, unused-import + import pennylane_lightning.lightning_qubit_ops LQ_CPP_BINARY_AVAILABLE = True except ImportError: From a64cf784d2c493c7edc5ce8b9222864360048cbe Mon Sep 17 00:00:00 2001 From: Dev version update bot Date: Thu, 22 Feb 2024 15:29:10 +0000 Subject: [PATCH 056/428] Auto update version --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index a223e801fa..92ab0f4480 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.35.0-dev16" +__version__ = "0.35.0-dev17" From ad1e3a45955656adef6748e2971199f4e899d591 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Thu, 22 Feb 2024 14:38:18 -0500 Subject: [PATCH 057/428] remove print --- tests/test_execute.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_execute.py b/tests/test_execute.py index b8278be610..9efc2955dc 100644 --- a/tests/test_execute.py +++ b/tests/test_execute.py @@ -117,7 +117,6 @@ def circuit(omega): zip([i for i in range(len(index) - 1, -1, -1)], index), 0, ) - print(prob) assert np.allclose(np.sum(prob), 1.0) assert prob[index] > 0.95 assert np.sum(prob) - prob[index] < 0.05 From aec5c070932bf3e376b76597117e1b68eb7679ff Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Thu, 22 Feb 2024 14:38:42 -0500 Subject: [PATCH 058/428] update state vector class --- .../lightning_qubit/_state_vector.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/_state_vector.py b/pennylane_lightning/lightning_qubit/_state_vector.py index 5d82aa6b34..b864381c07 100644 --- a/pennylane_lightning/lightning_qubit/_state_vector.py +++ b/pennylane_lightning/lightning_qubit/_state_vector.py @@ -35,6 +35,7 @@ from pennylane import ( BasisState, StatePrep, + DeviceError, ) @@ -54,6 +55,13 @@ def __init__(self, num_wires, dtype=np.complex128, device_name="lightning.qubit" self.num_wires = num_wires self._wires = Wires(range(num_wires)) self._dtype = dtype + + if dtype not in [np.complex64, np.complex128]: + raise TypeError(f"Unsupported complex type: {dtype}") + + if device_name != "lightning.qubit": + raise DeviceError(f'The device name "{device_name}" is not a valid option.') + self._name = device_name self._qubit_state = self._state_dtype()(self.num_wires) @@ -165,9 +173,6 @@ def _preprocess_state_vector(self, state, device_wires): or broadcasted state of shape ``(batch_size, 2**len(wires))`` array[int]: indices for which the state is changed to input state vector elements """ - - # translate to wire labels used by device - # special case for integral types if state.dtype.kind == "i": state = qml.numpy.array(state, dtype=self.dtype) @@ -289,7 +294,7 @@ def _apply_lightning_controlled(self, operation): # To support older versions of PL method(operation.base.matrix, control_wires, control_values, target_wires, False) - def apply_lightning(self, operations): + def _apply_lightning(self, operations): """Apply a list of operations to the state tensor. Args: @@ -336,7 +341,7 @@ def apply_operations(self, operations): self._apply_basis_state(operations[0].parameters[0], operations[0].wires) operations = operations[1:] - self.apply_lightning(operations) + self._apply_lightning(operations) def get_final_state(self, circuit: QuantumScript): """ @@ -348,8 +353,7 @@ def get_final_state(self, circuit: QuantumScript): circuit (QuantumScript): The single circuit to simulate Returns: - Tuple: A tuple containing the Lightning final state handler of the quantum script and - whether the state has a batch dimension. + LightningStateVector: Lightning final state class. """ self.apply_operations(circuit.operations) From 286fd4ca44e1d47eb644a9b70046646207ae59cf Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Thu, 22 Feb 2024 14:39:01 -0500 Subject: [PATCH 059/428] add state vector class tests --- tests/lightning_qubit/test_state_vector.py | 417 +++++++++++++ tests/lightning_qubit2/test_state_vector.py | 658 -------------------- 2 files changed, 417 insertions(+), 658 deletions(-) create mode 100644 tests/lightning_qubit/test_state_vector.py delete mode 100644 tests/lightning_qubit2/test_state_vector.py diff --git a/tests/lightning_qubit/test_state_vector.py b/tests/lightning_qubit/test_state_vector.py new file mode 100644 index 0000000000..76527127ec --- /dev/null +++ b/tests/lightning_qubit/test_state_vector.py @@ -0,0 +1,417 @@ +# Copyright 2018-2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +Unit tests for the serialization helper functions. +""" + +import pytest +from conftest import LightningDevice # tested device + +import math +import numpy as np +import pennylane as qml +from pennylane.wires import Wires +from pennylane.tape import QuantumScript + +from pennylane_lightning.lightning_qubit._state_vector import LightningStateVector +from pennylane_lightning.lightning_qubit import LightningQubit + +if not LightningQubit._CPP_BINARY_AVAILABLE: + pytest.skip("No binary module found. Skipping.", allow_module_level=True) + +if LightningDevice != LightningQubit: + pytest.skip("Exclusive tests for lightning.qubit. Skipping.", allow_module_level=True) + + +@pytest.mark.parametrize("num_wires", [3, 10]) +@pytest.mark.parametrize("dtype", [np.complex64, np.complex128]) +@pytest.mark.parametrize("device_name", ["lightning.qubit"]) +def test_device_name_and_init(num_wires, dtype, device_name): + """Test the class initialization and returned properties.""" + state_vector = LightningStateVector(num_wires, dtype=dtype, device_name=device_name) + assert state_vector.dtype == dtype + assert state_vector.name == device_name + assert state_vector.wires == Wires(range(num_wires)) + + +def test_wrong_device_name(): + """Test an invalid device name""" + with pytest.raises(qml.DeviceError, match="The device name"): + LightningStateVector(3, device_name="thunder.qubit") + + +@pytest.mark.parametrize("dtype", [np.double]) +def test_wrong_dtype(dtype): + """Test if the class returns a TypeError for a wrong dtype""" + with pytest.raises(TypeError, match="Unsupported complex type:"): + assert LightningStateVector(3, dtype=dtype) + + +@pytest.mark.parametrize( + "operation,expected_output,par", + [ + (qml.BasisState, [0, 0, 1, 0], [1, 0]), + (qml.BasisState, [0, 0, 0, 1], [1, 1]), + (qml.QubitStateVector, [0, 0, 1, 0], [0, 0, 1, 0]), + (qml.QubitStateVector, [0, 0, 0, 1], [0, 0, 0, 1]), + (qml.StatePrep, [0, 0, 1, 0], [0, 0, 1, 0]), + (qml.StatePrep, [0, 0, 0, 1], [0, 0, 0, 1]), + ( + qml.StatePrep, + [1 / math.sqrt(3), 0, 1 / math.sqrt(3), 1 / math.sqrt(3)], + [1 / math.sqrt(3), 0, 1 / math.sqrt(3), 1 / math.sqrt(3)], + ), + ( + qml.StatePrep, + [1 / math.sqrt(3), 0, -1 / math.sqrt(3), 1 / math.sqrt(3)], + [1 / math.sqrt(3), 0, -1 / math.sqrt(3), 1 / math.sqrt(3)], + ), + ], +) +def test_apply_operation_state_preparation(tol, operation, expected_output, par): + """Tests that applying an operation yields the expected output state for single wire + operations that have no parameters.""" + + wires = 2 + state_vector = LightningStateVector(wires) + state_vector.apply_operations([operation(np.array(par), Wires(range(wires)))]) + + assert np.allclose(state_vector.state, np.array(expected_output), atol=tol, rtol=0) + + +test_data_no_parameters = [ + (qml.PauliX, [1, 0], [0, 1]), + (qml.PauliX, [1 / math.sqrt(2), 1 / math.sqrt(2)], [1 / math.sqrt(2), 1 / math.sqrt(2)]), + (qml.PauliY, [1, 0], [0, 1j]), + (qml.PauliY, [1 / math.sqrt(2), 1 / math.sqrt(2)], [-1j / math.sqrt(2), 1j / math.sqrt(2)]), + (qml.PauliZ, [1, 0], [1, 0]), + (qml.PauliZ, [1 / math.sqrt(2), 1 / math.sqrt(2)], [1 / math.sqrt(2), -1 / math.sqrt(2)]), + (qml.S, [1, 0], [1, 0]), + (qml.S, [1 / math.sqrt(2), 1 / math.sqrt(2)], [1 / math.sqrt(2), 1j / math.sqrt(2)]), + (qml.T, [1, 0], [1, 0]), + ( + qml.T, + [1 / math.sqrt(2), 1 / math.sqrt(2)], + [1 / math.sqrt(2), np.exp(1j * np.pi / 4) / math.sqrt(2)], + ), + (qml.Hadamard, [1, 0], [1 / math.sqrt(2), 1 / math.sqrt(2)]), + (qml.Hadamard, [1 / math.sqrt(2), -1 / math.sqrt(2)], [0, 1]), + (qml.Identity, [1, 0], [1, 0]), + (qml.Identity, [1 / math.sqrt(2), 1 / math.sqrt(2)], [1 / math.sqrt(2), 1 / math.sqrt(2)]), +] + + +@pytest.mark.parametrize("operation,input,expected_output", test_data_no_parameters) +def test_apply_operation_single_wire_no_parameters(tol, operation, input, expected_output): + """Tests that applying an operation yields the expected output state for single wire + operations that have no parameters.""" + wires = 1 + state_vector = LightningStateVector(wires) + state_vector.apply_operations( + [qml.StatePrep(np.array(input), Wires(range(wires))), operation(Wires(range(wires)))] + ) + + assert np.allclose(state_vector.state, np.array(expected_output), atol=tol, rtol=0) + assert state_vector.state.dtype == state_vector.dtype + + +test_data_two_wires_no_parameters = [ + (qml.CNOT, [1, 0, 0, 0], [1, 0, 0, 0]), + (qml.CNOT, [0, 0, 1, 0], [0, 0, 0, 1]), + ( + qml.CNOT, + [1 / math.sqrt(2), 0, 0, 1 / math.sqrt(2)], + [1 / math.sqrt(2), 0, 1 / math.sqrt(2), 0], + ), + (qml.SWAP, [1, 0, 0, 0], [1, 0, 0, 0]), + (qml.SWAP, [0, 0, 1, 0], [0, 1, 0, 0]), + ( + qml.SWAP, + [1 / math.sqrt(2), 0, -1 / math.sqrt(2), 0], + [1 / math.sqrt(2), -1 / math.sqrt(2), 0, 0], + ), + (qml.CZ, [1, 0, 0, 0], [1, 0, 0, 0]), + (qml.CZ, [0, 0, 0, 1], [0, 0, 0, -1]), + ( + qml.CZ, + [1 / math.sqrt(2), 0, 0, -1 / math.sqrt(2)], + [1 / math.sqrt(2), 0, 0, 1 / math.sqrt(2)], + ), +] + + +@pytest.mark.parametrize("operation,input,expected_output", test_data_two_wires_no_parameters) +def test_apply_operation_two_wires_no_parameters(tol, operation, input, expected_output): + """Tests that applying an operation yields the expected output state for two wire + operations that have no parameters.""" + wires = 2 + state_vector = LightningStateVector(wires) + state_vector.apply_operations( + [qml.StatePrep(np.array(input), Wires(range(wires))), operation(Wires(range(wires)))] + ) + + assert np.allclose(state_vector.state, np.array(expected_output), atol=tol, rtol=0) + assert state_vector.state.dtype == state_vector.dtype + + +test_data_three_wires_no_parameters = [ + (qml.CSWAP, [1, 0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 0]), + (qml.CSWAP, [0, 0, 0, 0, 0, 1, 0, 0], [0, 0, 0, 0, 0, 0, 1, 0]), + (qml.CSWAP, [0, 0, 0, 0, 0, 0, 1, 0], [0, 0, 0, 0, 0, 1, 0, 0]), + (qml.Toffoli, [1, 0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 0]), + (qml.Toffoli, [0, 1, 0, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0, 0, 0]), + (qml.Toffoli, [0, 0, 0, 0, 0, 0, 1, 0], [0, 0, 0, 0, 0, 0, 0, 1]), + (qml.Toffoli, [0, 0, 0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 0, 1, 0]), +] + + +@pytest.mark.parametrize("operation,input,expected_output", test_data_three_wires_no_parameters) +def test_apply_operation_three_wires_no_parameters(tol, operation, input, expected_output): + """Tests that applying an operation yields the expected output state for three wire + operations that have no parameters.""" + + wires = 3 + state_vector = LightningStateVector(wires) + state_vector.apply_operations( + [qml.StatePrep(np.array(input), Wires(range(wires))), operation(Wires(range(wires)))] + ) + + assert np.allclose(state_vector.state, np.array(expected_output), atol=tol, rtol=0) + assert state_vector.state.dtype == state_vector.dtype + + +""" operation,input,expected_output,par """ +test_data_single_wire_with_parameters = [ + (qml.PhaseShift, [1, 0], [1, 0], [math.pi / 2]), + (qml.PhaseShift, [0, 1], [0, 1j], [math.pi / 2]), + ( + qml.PhaseShift, + [1 / math.sqrt(2), 1 / math.sqrt(2)], + [1 / math.sqrt(2), 1 / 2 + 1j / 2], + [math.pi / 4], + ), + (qml.RX, [1, 0], [1 / math.sqrt(2), -1j * 1 / math.sqrt(2)], [math.pi / 2]), + (qml.RX, [1, 0], [0, -1j], [math.pi]), + ( + qml.RX, + [1 / math.sqrt(2), 1 / math.sqrt(2)], + [1 / 2 - 1j / 2, 1 / 2 - 1j / 2], + [math.pi / 2], + ), + (qml.RY, [1, 0], [1 / math.sqrt(2), 1 / math.sqrt(2)], [math.pi / 2]), + (qml.RY, [1, 0], [0, 1], [math.pi]), + (qml.RY, [1 / math.sqrt(2), 1 / math.sqrt(2)], [0, 1], [math.pi / 2]), + (qml.RZ, [1, 0], [1 / math.sqrt(2) - 1j / math.sqrt(2), 0], [math.pi / 2]), + (qml.RZ, [0, 1], [0, 1j], [math.pi]), + ( + qml.RZ, + [1 / math.sqrt(2), 1 / math.sqrt(2)], + [1 / 2 - 1j / 2, 1 / 2 + 1j / 2], + [math.pi / 2], + ), + (qml.MultiRZ, [1, 0], [1 / math.sqrt(2) - 1j / math.sqrt(2), 0], [math.pi / 2]), + (qml.MultiRZ, [0, 1], [0, 1j], [math.pi]), + ( + qml.MultiRZ, + [1 / math.sqrt(2), 1 / math.sqrt(2)], + [1 / 2 - 1j / 2, 1 / 2 + 1j / 2], + [math.pi / 2], + ), + (qml.Rot, [1, 0], [1 / math.sqrt(2) - 1j / math.sqrt(2), 0], [math.pi / 2, 0, 0]), + (qml.Rot, [1, 0], [1 / math.sqrt(2), 1 / math.sqrt(2)], [0, math.pi / 2, 0]), + ( + qml.Rot, + [1 / math.sqrt(2), 1 / math.sqrt(2)], + [1 / 2 - 1j / 2, 1 / 2 + 1j / 2], + [0, 0, math.pi / 2], + ), + ( + qml.Rot, + [1, 0], + [-1j / math.sqrt(2), -1 / math.sqrt(2)], + [math.pi / 2, -math.pi / 2, math.pi / 2], + ), + ( + qml.Rot, + [1 / math.sqrt(2), 1 / math.sqrt(2)], + [1 / 2 + 1j / 2, -1 / 2 + 1j / 2], + [-math.pi / 2, math.pi, math.pi], + ), +] + + +@pytest.mark.parametrize( + "operation,input,expected_output,par", test_data_single_wire_with_parameters +) +def test_apply_operation_single_wire_with_parameters(tol, operation, input, expected_output, par): + """Tests that applying an operation yields the expected output state for single wire + operations that have parameters.""" + + wires = 1 + state_vector = LightningStateVector(wires) + state_vector.apply_operations( + [qml.StatePrep(np.array(input), Wires(range(wires))), operation(*par, Wires(range(wires)))] + ) + + assert np.allclose(state_vector.state, np.array(expected_output), atol=tol, rtol=0) + assert state_vector.state.dtype == state_vector.dtype + + +""" operation,input,expected_output,par """ +test_data_two_wires_with_parameters = [ + (qml.IsingXX, [1, 0, 0, 0], [1 / math.sqrt(2), 0, 0, -1j / math.sqrt(2)], [math.pi / 2]), + ( + qml.IsingXX, + [0, 1 / math.sqrt(2), 0, 1 / math.sqrt(2)], + [-0.5j, 0.5, -0.5j, 0.5], + [math.pi / 2], + ), + (qml.IsingXY, [1, 0, 0, 0], [1, 0, 0, 0], [math.pi / 2]), + ( + qml.IsingXY, + [0, 1 / math.sqrt(2), 0, 1 / math.sqrt(2)], + [0, 0.5, 0.5j, 1 / math.sqrt(2)], + [math.pi / 2], + ), + (qml.IsingYY, [1, 0, 0, 0], [1 / math.sqrt(2), 0, 0, 1j / math.sqrt(2)], [math.pi / 2]), + ( + qml.IsingYY, + [1 / math.sqrt(2), 0, 0, 1 / math.sqrt(2)], + [0.5 + 0.5j, 0, 0, 0.5 + 0.5j], + [math.pi / 2], + ), + (qml.IsingZZ, [1, 0, 0, 0], [1 / math.sqrt(2) - 1j / math.sqrt(2), 0, 0, 0], [math.pi / 2]), + ( + qml.IsingZZ, + [1 / math.sqrt(2), 0, 0, 1 / math.sqrt(2)], + [0.5 - 0.5j, 0, 0, 0.5 - 0.5j], + [math.pi / 2], + ), + (qml.MultiRZ, [1, 0, 0, 0], [1 / math.sqrt(2) - 1j / math.sqrt(2), 0, 0, 0], [math.pi / 2]), + ( + qml.MultiRZ, + [1 / math.sqrt(2), 0, 0, 1 / math.sqrt(2)], + [0.5 - 0.5j, 0, 0, 0.5 - 0.5j], + [math.pi / 2], + ), + (qml.CRX, [0, 1, 0, 0], [0, 1, 0, 0], [math.pi / 2]), + (qml.CRX, [0, 0, 0, 1], [0, 0, -1j, 0], [math.pi]), + ( + qml.CRX, + [0, 1 / math.sqrt(2), 1 / math.sqrt(2), 0], + [0, 1 / math.sqrt(2), 1 / 2, -1j / 2], + [math.pi / 2], + ), + (qml.CRY, [0, 0, 0, 1], [0, 0, -1 / math.sqrt(2), 1 / math.sqrt(2)], [math.pi / 2]), + (qml.CRY, [0, 0, 0, 1], [0, 0, -1, 0], [math.pi]), + ( + qml.CRY, + [1 / math.sqrt(2), 1 / math.sqrt(2), 0, 0], + [1 / math.sqrt(2), 1 / math.sqrt(2), 0, 0], + [math.pi / 2], + ), + (qml.CRZ, [0, 0, 0, 1], [0, 0, 0, 1 / math.sqrt(2) + 1j / math.sqrt(2)], [math.pi / 2]), + (qml.CRZ, [0, 0, 0, 1], [0, 0, 0, 1j], [math.pi]), + ( + qml.CRZ, + [1 / math.sqrt(2), 1 / math.sqrt(2), 0, 0], + [1 / math.sqrt(2), 1 / math.sqrt(2), 0, 0], + [math.pi / 2], + ), + ( + qml.CRot, + [0, 0, 0, 1], + [0, 0, 0, 1 / math.sqrt(2) + 1j / math.sqrt(2)], + [math.pi / 2, 0, 0], + ), + (qml.CRot, [0, 0, 0, 1], [0, 0, -1 / math.sqrt(2), 1 / math.sqrt(2)], [0, math.pi / 2, 0]), + ( + qml.CRot, + [0, 0, 1 / math.sqrt(2), 1 / math.sqrt(2)], + [0, 0, 1 / 2 - 1j / 2, 1 / 2 + 1j / 2], + [0, 0, math.pi / 2], + ), + ( + qml.CRot, + [0, 0, 0, 1], + [0, 0, 1 / math.sqrt(2), 1j / math.sqrt(2)], + [math.pi / 2, -math.pi / 2, math.pi / 2], + ), + ( + qml.CRot, + [0, 1 / math.sqrt(2), 1 / math.sqrt(2), 0], + [0, 1 / math.sqrt(2), 0, -1 / 2 + 1j / 2], + [-math.pi / 2, math.pi, math.pi], + ), + ( + qml.ControlledPhaseShift, + [1, 0, 0, 0], + [1, 0, 0, 0], + [math.pi / 2], + ), + ( + qml.ControlledPhaseShift, + [0, 1, 0, 0], + [0, 1, 0, 0], + [math.pi / 2], + ), + ( + qml.ControlledPhaseShift, + [0, 0, 1, 0], + [0, 0, 1, 0], + [math.pi / 2], + ), + ( + qml.ControlledPhaseShift, + [0, 0, 0, 1], + [0, 0, 0, 1 / math.sqrt(2) + 1j / math.sqrt(2)], + [math.pi / 4], + ), + ( + qml.ControlledPhaseShift, + [1 / math.sqrt(4), 1 / math.sqrt(4), 1 / math.sqrt(4), 1 / math.sqrt(4)], + [0.5, 0.5, 0.5, 1 / math.sqrt(8) + 1j / math.sqrt(8)], + [math.pi / 4], + ), +] + + +@pytest.mark.parametrize("operation,input,expected_output,par", test_data_two_wires_with_parameters) +def test_apply_operation_two_wires_with_parameters(tol, operation, input, expected_output, par): + """Tests that applying an operation yields the expected output state for two wire + operations that have parameters.""" + wires = 2 + state_vector = LightningStateVector(wires) + state_vector.apply_operations( + [qml.StatePrep(np.array(input), Wires(range(wires))), operation(*par, Wires(range(wires)))] + ) + + assert np.allclose(state_vector.state, np.array(expected_output), atol=tol, rtol=0) + assert state_vector.state.dtype == state_vector.dtype + + +@pytest.mark.parametrize("operation,input,expected_output,par", test_data_two_wires_with_parameters) +def test_get_final_state(tol, operation, input, expected_output, par): + """Tests that applying an operation yields the expected output state for two wire + operations that have parameters.""" + wires = 2 + state_vector = LightningStateVector(wires) + tape = QuantumScript( + [qml.StatePrep(np.array(input), Wires(range(wires))), operation(*par, Wires(range(wires)))] + ) + final_state = state_vector.get_final_state(tape) + + assert np.allclose(final_state.state, np.array(expected_output), atol=tol, rtol=0) + assert final_state.state.dtype == final_state.dtype + assert final_state == state_vector diff --git a/tests/lightning_qubit2/test_state_vector.py b/tests/lightning_qubit2/test_state_vector.py deleted file mode 100644 index e81f5c1783..0000000000 --- a/tests/lightning_qubit2/test_state_vector.py +++ /dev/null @@ -1,658 +0,0 @@ -# Copyright 2018-2024 Xanadu Quantum Technologies Inc. - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -""" -Unit tests for the serialization helper functions. -""" -import pytest -from conftest import device_name, LightningDevice - -import numpy as np -import pennylane as qml -from pennylane_lightning.core._serialize import QuantumScriptSerializer, global_phase_diagonal - -if not LightningDevice._CPP_BINARY_AVAILABLE: - pytest.skip("No binary module found. Skipping.", allow_module_level=True) - -if device_name == "lightning.kokkos": - from pennylane_lightning.lightning_kokkos_ops.observables import ( - NamedObsC64, - NamedObsC128, - HermitianObsC64, - HermitianObsC128, - TensorProdObsC64, - TensorProdObsC128, - HamiltonianC64, - HamiltonianC128, - SparseHamiltonianC64, - SparseHamiltonianC128, - ) -elif device_name == "lightning.gpu": - from pennylane_lightning.lightning_gpu_ops.observables import ( - NamedObsC64, - NamedObsC128, - HermitianObsC64, - HermitianObsC128, - TensorProdObsC64, - TensorProdObsC128, - HamiltonianC64, - HamiltonianC128, - SparseHamiltonianC64, - SparseHamiltonianC128, - ) -else: - from pennylane_lightning.lightning_qubit_ops.observables import ( - NamedObsC64, - NamedObsC128, - HermitianObsC64, - HermitianObsC128, - TensorProdObsC64, - TensorProdObsC128, - HamiltonianC64, - HamiltonianC128, - SparseHamiltonianC64, - SparseHamiltonianC128, - ) - - -def test_wrong_device_name(): - """Test the device name is not a valid option""" - - with pytest.raises(qml.DeviceError, match="The device name"): - QuantumScriptSerializer("thunder.qubit") - - -@pytest.mark.parametrize( - "obs,obs_type", - [ - (qml.PauliZ(0), NamedObsC128), - (qml.PauliZ(0) @ qml.PauliZ(1), TensorProdObsC128), - (qml.Hadamard(0), NamedObsC128), - (qml.Hermitian(np.eye(2), wires=0), HermitianObsC128), - ( - qml.PauliZ(0) @ qml.Hadamard(1) @ (0.1 * (qml.PauliZ(2) + qml.PauliX(3))), - HamiltonianC128, - ), - ( - ( - qml.Hermitian(np.eye(2), wires=0) - @ qml.Hermitian(np.eye(2), wires=1) - @ qml.Projector([0], wires=2) - ), - TensorProdObsC128, - ), - ( - qml.PauliZ(0) @ qml.Hermitian(np.eye(2), wires=1) @ qml.Projector([0], wires=2), - TensorProdObsC128, - ), - (qml.Projector([0], wires=0), HermitianObsC128), - (qml.Hamiltonian([1], [qml.PauliZ(0)]), HamiltonianC128), - (qml.sum(qml.Hadamard(0), qml.PauliX(1)), HermitianObsC128), - ( - qml.SparseHamiltonian(qml.Hamiltonian([1], [qml.PauliZ(0)]).sparse_matrix(), wires=[0]), - SparseHamiltonianC128, - ), - ], -) -def test_obs_returns_expected_type(obs, obs_type): - """Tests that observables get serialized to the expected type, with and without wires map""" - assert isinstance( - QuantumScriptSerializer(device_name)._ob(obs, dict(enumerate(obs.wires))), obs_type - ) - assert isinstance(QuantumScriptSerializer(device_name)._ob(obs), obs_type) - - -class TestSerializeObs: - """Tests for the _observables function""" - - wires_dict = {i: i for i in range(10)} - - @pytest.mark.parametrize("use_csingle", [True, False]) - @pytest.mark.parametrize("wires_map", [wires_dict, None]) - def test_tensor_non_tensor_return(self, use_csingle, wires_map): - """Test expected serialization for a mixture of tensor product and non-tensor product - return""" - with qml.tape.QuantumTape() as tape: - qml.expval(qml.PauliZ(0) @ qml.PauliX(1)) - qml.expval(qml.Hadamard(1)) - - tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 - named_obs = NamedObsC64 if use_csingle else NamedObsC128 - - s_expected = [ - tensor_prod_obs([named_obs("PauliZ", [0]), named_obs("PauliX", [1])]), - named_obs("Hadamard", [1]), - ] - - s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables( - tape, wires_map - ) - assert s == s_expected - - @pytest.mark.parametrize("use_csingle", [True, False]) - @pytest.mark.parametrize("wires_map", [wires_dict, None]) - def test_hermitian_return(self, use_csingle, wires_map): - """Test expected serialization for a Hermitian return""" - with qml.tape.QuantumTape() as tape: - qml.expval(qml.Hermitian(np.eye(4), wires=[0, 1])) - - hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 - c_dtype = np.complex64 if use_csingle else np.complex128 - - s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables( - tape, wires_map - ) - s_expected = hermitian_obs( - np.array( - [1.0, 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, 1.0], - dtype=c_dtype, - ), - [0, 1], - ) - assert s[0] == s_expected - - @pytest.mark.parametrize("use_csingle", [True, False]) - @pytest.mark.parametrize("wires_map", [wires_dict, None]) - def test_hermitian_tensor_return(self, use_csingle, wires_map): - """Test expected serialization for a Hermitian return""" - with qml.tape.QuantumTape() as tape: - qml.expval(qml.Hermitian(np.eye(4), wires=[0, 1]) @ qml.Hermitian(np.eye(2), wires=[2])) - - c_dtype = np.complex64 if use_csingle else np.complex128 - tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 - hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 - s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables( - tape, wires_map - ) - - s_expected = tensor_prod_obs( - [ - hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), - hermitian_obs(np.eye(2, dtype=c_dtype).ravel(), [2]), - ] - ) - - assert s[0] == s_expected - - @pytest.mark.parametrize("use_csingle", [True, False]) - @pytest.mark.parametrize("wires_map", [wires_dict, None]) - def test_mixed_tensor_return(self, use_csingle, wires_map): - """Test expected serialization for a mixture of Hermitian and Pauli return""" - with qml.tape.QuantumTape() as tape: - qml.expval(qml.Hermitian(np.eye(4), wires=[0, 1]) @ qml.PauliY(2)) - - c_dtype = np.complex64 if use_csingle else np.complex128 - tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 - hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 - named_obs = NamedObsC64 if use_csingle else NamedObsC128 - - s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables( - tape, wires_map - ) - - s_expected = tensor_prod_obs( - [hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), named_obs("PauliY", [2])] - ) - - assert s[0] == s_expected - - @pytest.mark.parametrize("use_csingle", [True, False]) - @pytest.mark.parametrize("wires_map", [wires_dict, None]) - def test_hamiltonian_return(self, use_csingle, wires_map): - """Test expected serialization for a Hamiltonian return""" - - ham = qml.Hamiltonian( - [0.3, 0.5, 0.4], - [ - qml.Hermitian(np.eye(4), wires=[0, 1]) @ qml.PauliY(2), - qml.PauliX(0) @ qml.PauliY(2), - qml.Hermitian(np.ones((8, 8)), wires=range(3)), - ], - ) - - with qml.tape.QuantumTape() as tape: - qml.expval(ham) - - hamiltonian_obs = HamiltonianC64 if use_csingle else HamiltonianC128 - named_obs = NamedObsC64 if use_csingle else NamedObsC128 - hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 - tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 - r_dtype = np.float32 if use_csingle else np.float64 - c_dtype = np.complex64 if use_csingle else np.complex128 - - s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables( - tape, wires_map - ) - - s_expected = hamiltonian_obs( - np.array([0.3, 0.5, 0.4], dtype=r_dtype), - [ - tensor_prod_obs( - [ - hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), - named_obs("PauliY", [2]), - ] - ), - tensor_prod_obs([named_obs("PauliX", [0]), named_obs("PauliY", [2])]), - hermitian_obs(np.ones(64, dtype=c_dtype), [0, 1, 2]), - ], - ) - - assert s[0] == s_expected - - @pytest.mark.parametrize("use_csingle", [True, False]) - @pytest.mark.parametrize("wires_map", [wires_dict, None]) - def test_hamiltonian_tensor_return(self, use_csingle, wires_map): - """Test expected serialization for a Hamiltonian return""" - - with qml.tape.QuantumTape() as tape: - ham = qml.Hamiltonian( - [0.3, 0.5, 0.4], - [ - qml.Hermitian(np.eye(4), wires=[0, 1]) @ qml.PauliY(2), - qml.PauliX(0) @ qml.PauliY(2), - qml.Hermitian(np.ones((8, 8)), wires=range(3)), - ], - ) - qml.expval(ham @ qml.PauliZ(3)) - - hamiltonian_obs = HamiltonianC64 if use_csingle else HamiltonianC128 - named_obs = NamedObsC64 if use_csingle else NamedObsC128 - hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 - tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 - r_dtype = np.float32 if use_csingle else np.float64 - c_dtype = np.complex64 if use_csingle else np.complex128 - - s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables( - tape, wires_map - ) - - # Expression (ham @ obs) is converted internally by Pennylane - # where obs is appended to each term of the ham - s_expected = hamiltonian_obs( - np.array([0.3, 0.5, 0.4], dtype=r_dtype), - [ - tensor_prod_obs( - [ - hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), - named_obs("PauliY", [2]), - named_obs("PauliZ", [3]), - ] - ), - tensor_prod_obs( - [named_obs("PauliX", [0]), named_obs("PauliY", [2]), named_obs("PauliZ", [3])] - ), - tensor_prod_obs( - [hermitian_obs(np.ones(64, dtype=c_dtype), [0, 1, 2]), named_obs("PauliZ", [3])] - ), - ], - ) - - assert s[0] == s_expected - - @pytest.mark.parametrize("use_csingle", [True, False]) - @pytest.mark.parametrize("wires_map", [wires_dict, None]) - def test_hamiltonian_mix_return(self, use_csingle, wires_map): - """Test expected serialization for a Hamiltonian return""" - - ham1 = qml.Hamiltonian( - [0.3, 0.5, 0.4], - [ - qml.Hermitian(np.eye(4), wires=[0, 1]) @ qml.PauliY(2), - qml.PauliX(0) @ qml.PauliY(2), - qml.Hermitian(np.ones((8, 8)), wires=range(3)), - ], - ) - ham2 = qml.Hamiltonian( - [0.7, 0.3], - [qml.PauliX(0) @ qml.Hermitian(np.eye(4), wires=[1, 2]), qml.PauliY(0) @ qml.PauliX(2)], - ) - - with qml.tape.QuantumTape() as tape: - qml.expval(ham1) - qml.expval(ham2) - - hamiltonian_obs = HamiltonianC64 if use_csingle else HamiltonianC128 - named_obs = NamedObsC64 if use_csingle else NamedObsC128 - hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 - tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 - r_dtype = np.float32 if use_csingle else np.float64 - c_dtype = np.complex64 if use_csingle else np.complex128 - - s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables( - tape, wires_map - ) - - s_expected1 = hamiltonian_obs( - np.array([0.3, 0.5, 0.4], dtype=r_dtype), - [ - tensor_prod_obs( - [ - hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), - named_obs("PauliY", [2]), - ] - ), - tensor_prod_obs([named_obs("PauliX", [0]), named_obs("PauliY", [2])]), - hermitian_obs(np.ones(64, dtype=c_dtype), [0, 1, 2]), - ], - ) - s_expected2 = hamiltonian_obs( - np.array([0.7, 0.3], dtype=r_dtype), - [ - tensor_prod_obs( - [ - named_obs("PauliX", [0]), - hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [1, 2]), - ] - ), - tensor_prod_obs([named_obs("PauliY", [0]), named_obs("PauliX", [2])]), - ], - ) - - assert s[0] == s_expected1 - assert s[1] == s_expected2 - - @pytest.mark.parametrize( - "obs,coeffs,terms", - [ - (qml.prod(qml.PauliZ(0), qml.PauliX(1)), [1], [[("PauliX", 1), ("PauliZ", 0)]]), - (qml.s_prod(0.1, qml.PauliX(0)), [0.1], ("PauliX", 0)), - ( - qml.sum( - 0.5 * qml.prod(qml.PauliX(0), qml.PauliZ(1)), - 0.1 * qml.prod(qml.PauliZ(0), qml.PauliY(1)), - ), - [0.5, 0.1], - [[("PauliZ", 1), ("PauliX", 0)], [("PauliY", 1), ("PauliZ", 0)]], - ), - ], - ) - @pytest.mark.parametrize("use_csingle", [True, False]) - @pytest.mark.parametrize("wires_map", [wires_dict, None]) - def test_op_arithmetic_uses_hamiltonian(self, use_csingle, obs, coeffs, terms, wires_map): - """Tests that an arithmetic obs with a PauliRep serializes as a Hamiltonian.""" - tape = qml.tape.QuantumTape(measurements=[qml.expval(obs)]) - res, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables( - tape, wires_map - ) - assert len(res) == 1 - assert isinstance(res[0], HamiltonianC64 if use_csingle else HamiltonianC128) - - hamiltonian_obs = HamiltonianC64 if use_csingle else HamiltonianC128 - tensor_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 - named_obs = NamedObsC64 if use_csingle else NamedObsC128 - rtype = np.float32 if use_csingle else np.float64 - term_shape = np.array(terms).shape - - if len(term_shape) == 1: # just a single pauli op - expected_terms = [named_obs(terms[0], [terms[1]])] - elif len(term_shape) == 3: # list of tensor products - expected_terms = [ - tensor_obs([named_obs(pauli, [wire]) for pauli, wire in term]) for term in terms - ] - - coeffs = np.array(coeffs).astype(rtype) - assert res[0] == hamiltonian_obs(coeffs, expected_terms) - - @pytest.mark.parametrize("use_csingle", [True, False]) - @pytest.mark.parametrize("wires_map", [wires_dict, None]) - def test_multi_wire_identity(self, use_csingle, wires_map): - """Tests that multi-wire Identity does not fail serialization.""" - tape = qml.tape.QuantumTape(measurements=[qml.expval(qml.Identity(wires=[1, 2]))]) - res, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables( - tape, wires_map - ) - assert len(res) == 1 - - named_obs = NamedObsC64 if use_csingle else NamedObsC128 - assert res[0] == named_obs("Identity", [1]) - - -class TestSerializeOps: - """Tests for the _ops function""" - - wires_dict = {i: i for i in range(10)} - - @pytest.mark.parametrize("wires_map", [wires_dict, None]) - def test_basic_circuit(self, wires_map): - """Test expected serialization for a simple circuit""" - with qml.tape.QuantumTape() as tape: - qml.RX(0.4, wires=0) - qml.RY(0.6, wires=1) - qml.CNOT(wires=[0, 1]) - - s = QuantumScriptSerializer(device_name).serialize_ops(tape, wires_map) - s_expected = ( - ( - ["RX", "RY", "CNOT"], - [np.array([0.4]), np.array([0.6]), []], - [[0], [1], [0, 1]], - [False, False, False], - [[], [], []], - [[], [], []], - [[], [], []], - ), - False, - ) - assert s == s_expected - - @pytest.mark.parametrize("wires_map", [wires_dict, None]) - def test_basic_circuit_not_implemented_ctrl_ops(self, wires_map): - """Test expected serialization for a simple circuit""" - ops = qml.OrbitalRotation(0.1234, wires=range(4)) - with qml.tape.QuantumTape() as tape: - qml.RX(0.4, wires=0) - qml.RY(0.6, wires=1) - qml.ctrl(ops, [4, 5]) - - s = QuantumScriptSerializer(device_name).serialize_ops(tape, wires_map) - s_expected = ( - ( - ["RX", "RY", "QubitUnitary"], - [np.array([0.4]), np.array([0.6]), [0.0]], - [[0], [1], list(ops.wires)], - [False, False, False], - [[], [], [qml.matrix(ops)]], - [[], [], [4, 5]], - ), - False, - ) - assert s[0][0] == s_expected[0][0] - assert s[0][1] == s_expected[0][1] - assert s[0][2] == s_expected[0][2] - assert s[0][3] == s_expected[0][3] - assert all(np.allclose(s0, s1) for s0, s1 in zip(s[0][4], s_expected[0][4])) - assert s[0][5] == s_expected[0][5] - assert s[1] == s_expected[1] - - @pytest.mark.parametrize("wires_map", [wires_dict, None]) - def test_multicontrolledx(self, wires_map): - """Test expected serialization for a simple circuit""" - with qml.tape.QuantumTape() as tape: - qml.RX(0.4, wires=0) - qml.RY(0.6, wires=1) - qml.ctrl(qml.PauliX(wires=0), [1, 2, 3], control_values=[True, False, False]) - - s = QuantumScriptSerializer(device_name).serialize_ops(tape, wires_map) - s_expected = ( - ( - ["RX", "RY", "PauliX"], - [np.array([0.4]), np.array([0.6]), []], - [[0], [1], [0]], - [False, False, False], - [[], [], []], - [[], [], [1, 2, 3]], - [[], [], [True, False, False]], - ), - False, - ) - assert s == s_expected - - @pytest.mark.parametrize("wires_map", [wires_dict, None]) - @pytest.mark.parametrize("stateprep", [qml.QubitStateVector, qml.StatePrep]) - def test_skips_prep_circuit(self, stateprep, wires_map): - """Test expected serialization for a simple circuit with state preparation, such that - the state preparation is skipped""" - with qml.tape.QuantumTape() as tape: - stateprep([1, 0], wires=0) - qml.BasisState([1], wires=1) - qml.RX(0.4, wires=0) - qml.RY(0.6, wires=1) - qml.CNOT(wires=[0, 1]) - - s = QuantumScriptSerializer(device_name).serialize_ops(tape, wires_map) - s_expected = ( - ( - ["RX", "RY", "CNOT"], - [[0.4], [0.6], []], - [[0], [1], [0, 1]], - [False, False, False], - [[], [], []], - [[], [], []], - [[], [], []], - ), - True, - ) - assert s == s_expected - - @pytest.mark.parametrize("wires_map", [wires_dict, None]) - def test_unsupported_kernel_circuit(self, wires_map): - """Test expected serialization for a circuit including gates that do not have a dedicated - kernel""" - with qml.tape.QuantumTape() as tape: - qml.CNOT(wires=[0, 1]) - qml.RZ(0.2, wires=2) - - s = QuantumScriptSerializer(device_name).serialize_ops(tape, wires_map) - s_expected = ( - ( - ["CNOT", "RZ"], - [[], [0.2]], - [[0, 1], [2]], - [False, False], - ), - False, - ) - assert s[0][0] == s_expected[0][0] - assert s[0][1] == s_expected[0][1] - - def test_custom_wires_circuit(self): - """Test expected serialization for a simple circuit with custom wire labels""" - wires_dict = {"a": 0, 3.2: 1} - with qml.tape.QuantumTape() as tape: - qml.RX(0.4, wires="a") - qml.RY(0.6, wires=3.2) - qml.CNOT(wires=["a", 3.2]) - qml.SingleExcitation(0.5, wires=["a", 3.2]) - qml.SingleExcitationPlus(0.4, wires=["a", 3.2]) - qml.adjoint(qml.SingleExcitationMinus(0.5, wires=["a", 3.2]), lazy=False) - - s = QuantumScriptSerializer(device_name).serialize_ops(tape, wires_dict) - s_expected = ( - ( - [ - "RX", - "RY", - "CNOT", - "SingleExcitation", - "SingleExcitationPlus", - "SingleExcitationMinus", - ], - [[0.4], [0.6], [], [0.5], [0.4], [-0.5]], - [[0], [1], [0, 1], [0, 1], [0, 1], [0, 1]], - [False, False, False, False, False, False], - [[], [], [], [], [], []], - [[], [], [], [], [], []], - [[], [], [], [], [], []], - ), - False, - ) - assert s == s_expected - - @pytest.mark.parametrize("wires_map", [wires_dict, None]) - @pytest.mark.parametrize("C", [True, False]) - def test_integration(self, C, wires_map): - """Test expected serialization for a random circuit""" - with qml.tape.QuantumTape() as tape: - qml.RX(0.4, wires=0) - qml.RY(0.6, wires=1) - qml.CNOT(wires=[0, 1]) - qml.QubitUnitary(np.eye(4), wires=[0, 1]) - qml.templates.QFT(wires=[0, 1, 2]) - qml.DoubleExcitation(0.555, wires=[3, 2, 1, 0]) - qml.DoubleExcitationMinus(0.555, wires=[0, 1, 2, 3]) - qml.DoubleExcitationPlus(0.555, wires=[0, 1, 2, 3]) - - s = QuantumScriptSerializer(device_name).serialize_ops(tape, wires_map) - - dtype = np.complex64 if C else np.complex128 - s_expected = ( - ( - [ - "RX", - "RY", - "CNOT", - "QubitUnitary", - "QFT", - "DoubleExcitation", - "DoubleExcitationMinus", - "DoubleExcitationPlus", - ], - [[0.4], [0.6], [], [0.0], [], [0.555], [0.555], [0.555]], - [[0], [1], [0, 1], [0, 1], [0, 1, 2], [3, 2, 1, 0], [0, 1, 2, 3], [0, 1, 2, 3]], - [False, False, False, False, False, False, False, False], - [ - [], - [], - [], - qml.matrix(qml.QubitUnitary(np.eye(4, dtype=dtype), wires=[0, 1])), - qml.matrix(qml.templates.QFT(wires=[0, 1, 2])), - [], - [], - [], - ], - ), - False, - ) - assert s[0][0] == s_expected[0][0] - assert s[0][1] == s_expected[0][1] - assert s[0][2] == s_expected[0][2] - assert s[0][3] == s_expected[0][3] - assert s[1] == s_expected[1] - - assert all(np.allclose(s1, s2) for s1, s2 in zip(s[0][4], s_expected[0][4])) - - -def check_global_phase_diagonal(par, wires, targets, controls, control_values): - op = qml.ctrl(qml.GlobalPhase(par, wires=targets), controls, control_values=control_values) - return np.diag(op.matrix(wires)) - - -def test_global_phase(): - """Validate global_phase_diagonal with various combinations of num_qubits, targets and controls.""" - import itertools - - nmax = 7 - par = 0.1 - for nq in range(2, nmax): - wires = range(nq) - for nw in range(nq, nmax): - wire_lists = list(itertools.permutations(wires, nw)) - for wire_list in wire_lists: - for i in range(len(wire_list) - 1): - targets = wire_list[0:i] - controls = wire_list[i:] - control_values = [i % 2 == 0 for i in controls] - D0 = check_global_phase_diagonal(par, wires, targets, controls, control_values) - D1 = global_phase_diagonal(par, wires, controls, control_values) - assert np.allclose(D0, D1) From 4ee66095d4e3c3e2ecc2a89de65b3d5a15afc673 Mon Sep 17 00:00:00 2001 From: albi3ro Date: Thu, 22 Feb 2024 17:44:36 -0500 Subject: [PATCH 060/428] adding measurement tests --- .../lightning_qubit/_measurements.py | 1 + tests/lightning_qubit2/test_measurements.py | 175 ++++++++++++++++++ 2 files changed, 176 insertions(+) create mode 100644 tests/lightning_qubit2/test_measurements.py diff --git a/pennylane_lightning/lightning_qubit/_measurements.py b/pennylane_lightning/lightning_qubit/_measurements.py index 120a6696bb..8554e43ea0 100644 --- a/pennylane_lightning/lightning_qubit/_measurements.py +++ b/pennylane_lightning/lightning_qubit/_measurements.py @@ -50,6 +50,7 @@ def __init__(self, qubit_state: LightningStateVector) -> None: self.state = qubit_state.state_vector self.dtype = qubit_state.dtype self.measurement_lightning = self._measurement_dtype()(self.state) + def _measurement_dtype(self): """Binding to Lightning Managed state vector. diff --git a/tests/lightning_qubit2/test_measurements.py b/tests/lightning_qubit2/test_measurements.py new file mode 100644 index 0000000000..9ea81bdabb --- /dev/null +++ b/tests/lightning_qubit2/test_measurements.py @@ -0,0 +1,175 @@ +# Copyright 2018-2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import pytest + +import numpy as np + +import pennylane as qml + +try: + from pennylane_lightning.lightning_qubit_ops import ( + MeasurementsC64, + MeasurementsC128, + ) +except ImportError: + pass + +from pennylane_lightning.lightning_qubit._state_vector import LightningStateVector +from pennylane_lightning.lightning_qubit._measurements import LightningMeasurements + + +class CustomStateMeasurment(qml.measurements.StateMeasurement): + + def process_state(self, state, wire_order): + return 1 + + +def test_initialization_complex64(): + """Tests for the initialization of the LightningMeasurements class with np.complex64.""" + statevector = LightningStateVector(num_wires=5, dtype=np.complex64) + m = LightningMeasurements(statevector) + + assert m._qubit_state is statevector + assert m.state is statevector.state_vector + assert m.dtype == np.complex64 + assert isinstance(m.measurement_lightning, MeasurementsC64) + + +def test_initialization_complex128(): + """Tests for the initialization of the LightningMeasurements class.""" + statevector = LightningStateVector(num_wires=5, dtype=np.complex128) + m = LightningMeasurements(statevector) + + assert m._qubit_state is statevector + assert m.state is statevector.state_vector + assert m.dtype == np.complex128 + assert isinstance(m.measurement_lightning, MeasurementsC128) + +class TestGetMeasurementFunction: + """Tests for the get_measurement_function method.""" + + def test_only_support_state_measurements(self): + """Test than a NotImplementedError is raised if the measurement is not a state measurement.""" + + statevector = LightningStateVector(num_wires=5, dtype=np.complex64) + m = LightningMeasurements(statevector) + + mp = qml.counts(wires=(0,1)) + with pytest.raises(NotImplementedError): + m.get_measurement_function(mp) + + @pytest.mark.parametrize("mp", (qml.probs(wires=0), + qml.var(qml.Z(0)), + qml.vn_entropy(wires=0), + CustomStateMeasurment(), + qml.expval(qml.Identity(0)), + qml.expval(qml.Projector([1, 0], wires=(0,1))) + )) + def test_state_diagonalizing_gates_measurements(self, mp): + """Test that any non-expval measurement is""" + statevector = LightningStateVector(num_wires=5, dtype=np.complex64) + m = LightningMeasurements(statevector) + + assert m.get_measurement_function(mp) == m.state_diagonalizing_gates + + @pytest.mark.parametrize("obs", ( + qml.X(0), + qml.Y(0), + qml.Z(0), + qml.sum(qml.X(0), qml.Y(0)), + qml.prod(qml.X(0), qml.Y(1)), + qml.s_prod(2.0, qml.X(0)), + qml.Hamiltonian([1.0, 2.0], [qml.X(0), qml.Y(0)]), + qml.Hermitian(np.eye(2), wires=0), + qml.SparseHamiltonian(qml.X.compute_sparse_matrix(), wires=0), + )) + def test_expval_selected(self, obs): + """Test that expval is chosen for a variety of different expectation values.""" + statevector = LightningStateVector(num_wires=5, dtype=np.complex64) + m = LightningMeasurements(statevector) + mp = qml.expval(obs) + assert m.get_measurement_function(mp) == m.expval + + +def expected_entropy_ising_xx(param): + """ + Return the analytical entropy for the IsingXX. + """ + eig_1 = (1 + np.sqrt(1 - 4 * np.cos(param / 2) ** 2 * np.sin(param / 2) ** 2)) / 2 + eig_2 = (1 - np.sqrt(1 - 4 * np.cos(param / 2) ** 2 * np.sin(param / 2) ** 2)) / 2 + eigs = [eig_1, eig_2] + eigs = [eig for eig in eigs if eig > 0] + + expected_entropy = eigs * np.log(eigs) + + expected_entropy = -np.sum(expected_entropy) + return expected_entropy + + +@pytest.mark.parametrize("method_name", ("state_diagonalizing_gates", "measurement")) +class TestStateDiagonalizingGates: + """Tests for various measurements that go through state_diagonalizing_gates""" + + def test_vn_entropy(self, method_name): + """Test that state_diagonalizing_gates can handle an arbitrary measurement process.""" + phi = 0.5 + statevector = LightningStateVector(num_wires=5, dtype=np.complex64) + statevector.apply_operations([qml.IsingXX(phi, wires=(0,1))]) + m = LightningMeasurements(statevector) + measurement = qml.vn_entropy(wires=0) + result = getattr(m, method_name)(measurement) + assert qml.math.allclose(result, expected_entropy_ising_xx(phi)) + + def test_custom_measurement(self, method_name): + """Test that LightningMeasurements can handle a custom state based measurement.""" + statevector = LightningStateVector(num_wires=5, dtype=np.complex64) + m = LightningMeasurements(statevector) + measurement = CustomStateMeasurment() + result = getattr(m, method_name)(measurement) + assert result == 1 + + def test_measurement_with_diagonalizing_gates(self, method_name): + statevector = LightningStateVector(num_wires=5, dtype=np.complex64) + m = LightningMeasurements(statevector) + measurement = qml.probs(op=qml.X(0)) + result = getattr(m, method_name)(measurement) + assert qml.math.allclose(result, [0.5, 0.5]) + + def test_identity_expval(self, method_name): + """Test that the expectation value of an identity is always one.""" + statevector = LightningStateVector(num_wires=5, dtype=np.complex64) + statevector.apply_operations([qml.Rot(0.5, 4.2, 6.8, wires=4)]) + m = LightningMeasurements(statevector) + result = getattr(m, method_name)(qml.expval(qml.I(4))) + assert np.allclose(result, 1.0) + + def test_basis_state_projector_expval(self, method_name): + """Test expectation value for a basis state projector.""" + phi = 0.8 + statevector = LightningStateVector(num_wires=1, dtype=np.complex64) + statevector.apply_operations([qml.RX(phi, 0)]) + m = LightningMeasurements(statevector) + result = getattr(m, method_name)(qml.expval(qml.Projector([0], wires=0))) + assert qml.math.allclose(result, np.cos(phi/2)**2) + + def test_state_vector_projector_expval(self, method_name): + """Test expectation value for a state vector projector.""" + phi = -0.6 + statevector = LightningStateVector(num_wires=1, dtype=np.complex64) + statevector.apply_operations([qml.RX(phi, 0)]) + m = LightningMeasurements(statevector) + result = getattr(m, method_name)(qml.expval(qml.Projector([0, 1], wires=0))) + assert qml.math.allclose(result, np.sin(phi/2)**2) + From e8b606e8cbbcf33753eb3fe862183f1e07486ae1 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Fri, 23 Feb 2024 08:43:35 -0500 Subject: [PATCH 061/428] update state vector and tests --- .../lightning_qubit/_state_vector.py | 50 ++++++++----------- tests/lightning_qubit/test_state_vector.py | 2 +- 2 files changed, 22 insertions(+), 30 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/_state_vector.py b/pennylane_lightning/lightning_qubit/_state_vector.py index b864381c07..0351f8277c 100644 --- a/pennylane_lightning/lightning_qubit/_state_vector.py +++ b/pennylane_lightning/lightning_qubit/_state_vector.py @@ -52,18 +52,18 @@ class LightningStateVector: """ def __init__(self, num_wires, dtype=np.complex128, device_name="lightning.qubit"): - self.num_wires = num_wires + self._num_wires = num_wires self._wires = Wires(range(num_wires)) self._dtype = dtype - if dtype not in [np.complex64, np.complex128]: + if dtype not in [np.complex64, np.complex128]: # pragma: no cover raise TypeError(f"Unsupported complex type: {dtype}") if device_name != "lightning.qubit": raise DeviceError(f'The device name "{device_name}" is not a valid option.') - self._name = device_name - self._qubit_state = self._state_dtype()(self.num_wires) + self._device_name = device_name + self._qubit_state = self._state_dtype()(self._num_wires) @property def dtype(self): @@ -71,9 +71,9 @@ def dtype(self): return self._dtype @property - def name(self): + def device_name(self): """Returns the state vector device name.""" - return self._name + return self._device_name @property def wires(self): @@ -88,10 +88,6 @@ def _state_dtype(self): Returns: the state vector class """ - if self.dtype not in [np.complex128, np.complex64]: # pragma: no cover - raise ValueError( - f"Data type is not supported for state-vector computation: {self.dtype}" - ) return StateVectorC128 if self.dtype == np.complex128 else StateVectorC64 @staticmethod @@ -150,7 +146,7 @@ def state(self): >>> print(dev.state) [0.+0.j 1.+0.j] """ - state = np.zeros(2**self.num_wires, dtype=self.dtype) + state = np.zeros(2**self._num_wires, dtype=self.dtype) state = self._asarray(state, dtype=self.dtype) self._qubit_state.getState(state) return state @@ -175,21 +171,21 @@ def _preprocess_state_vector(self, state, device_wires): """ # special case for integral types if state.dtype.kind == "i": - state = qml.numpy.array(state, dtype=self.dtype) + state = np.array(state, dtype=self.dtype) state = self._asarray(state, dtype=self.dtype) - if len(device_wires) == self.num_wires and Wires(sorted(device_wires)) == device_wires: + if len(device_wires) == self._num_wires and Wires(sorted(device_wires)) == device_wires: return None, state # generate basis states on subset of qubits via the cartesian product basis_states = np.array(list(product([0, 1], repeat=len(device_wires)))) # get basis states to alter on full set of qubits - unravelled_indices = np.zeros((2 ** len(device_wires), self.num_wires), dtype=int) + unravelled_indices = np.zeros((2 ** len(device_wires), self._num_wires), dtype=int) unravelled_indices[:, device_wires] = basis_states # get indices for which the state is changed to input state vector elements - ravelled_indices = np.ravel_multi_index(unravelled_indices.T, [2] * self.num_wires) + ravelled_indices = np.ravel_multi_index(unravelled_indices.T, [2] * self._num_wires) return ravelled_indices, state def _get_basis_state_index(self, state, wires): @@ -213,7 +209,7 @@ def _get_basis_state_index(self, state, wires): raise ValueError("BasisState parameter and wires must be of equal length.") # get computational basis state number - basis_states = 2 ** (self.num_wires - 1 - np.array(wires)) + basis_states = 2 ** (self._num_wires - 1 - np.array(wires)) basis_states = qml.math.convert_like(basis_states, state) return int(qml.math.dot(state, basis_states)) @@ -233,9 +229,9 @@ def _apply_state_vector(self, state, device_wires): ravelled_indices, state = self._preprocess_state_vector(state, device_wires) # translate to wire labels used by device - output_shape = [2] * self.num_wires + output_shape = [2] * self._num_wires - if len(device_wires) == self.num_wires and Wires(sorted(device_wires)) == device_wires: + if len(device_wires) == self._num_wires and Wires(sorted(device_wires)) == device_wires: # Initialize the entire device state with the input state state = np.reshape(state, output_shape).ravel(order="C") self._qubit_state.UpdateData(state) @@ -282,17 +278,13 @@ def _apply_lightning_controlled(self, operation): else: # apply gate as an n-controlled matrix method = getattr(state, "applyControlledMatrix") target_wires = self.wires.indices(operation.target_wires) - try: - method( - qml.matrix(operation.base), - control_wires, - control_values, - target_wires, - False, - ) - except AttributeError: # pragma: no cover - # To support older versions of PL - method(operation.base.matrix, control_wires, control_values, target_wires, False) + method( + qml.matrix(operation.base), + control_wires, + control_values, + target_wires, + False, + ) def _apply_lightning(self, operations): """Apply a list of operations to the state tensor. diff --git a/tests/lightning_qubit/test_state_vector.py b/tests/lightning_qubit/test_state_vector.py index 76527127ec..b7ba9197ce 100644 --- a/tests/lightning_qubit/test_state_vector.py +++ b/tests/lightning_qubit/test_state_vector.py @@ -41,7 +41,7 @@ def test_device_name_and_init(num_wires, dtype, device_name): """Test the class initialization and returned properties.""" state_vector = LightningStateVector(num_wires, dtype=dtype, device_name=device_name) assert state_vector.dtype == dtype - assert state_vector.name == device_name + assert state_vector.device_name == device_name assert state_vector.wires == Wires(range(num_wires)) From 7ab3adeb84720f50eb3c6e5943781ad00cecbe10 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Fri, 23 Feb 2024 08:58:48 -0500 Subject: [PATCH 062/428] move and rename test files, and format --- .../lightning_qubit/_measurements.py | 1 - .../test_measurements_class.py} | 60 ++++++++++--------- ...e_vector.py => test_state_vector_class.py} | 0 3 files changed, 33 insertions(+), 28 deletions(-) rename tests/{lightning_qubit2/test_measurements.py => lightning_qubit/test_measurements_class.py} (85%) rename tests/lightning_qubit/{test_state_vector.py => test_state_vector_class.py} (100%) diff --git a/pennylane_lightning/lightning_qubit/_measurements.py b/pennylane_lightning/lightning_qubit/_measurements.py index 8554e43ea0..120a6696bb 100644 --- a/pennylane_lightning/lightning_qubit/_measurements.py +++ b/pennylane_lightning/lightning_qubit/_measurements.py @@ -50,7 +50,6 @@ def __init__(self, qubit_state: LightningStateVector) -> None: self.state = qubit_state.state_vector self.dtype = qubit_state.dtype self.measurement_lightning = self._measurement_dtype()(self.state) - def _measurement_dtype(self): """Binding to Lightning Managed state vector. diff --git a/tests/lightning_qubit2/test_measurements.py b/tests/lightning_qubit/test_measurements_class.py similarity index 85% rename from tests/lightning_qubit2/test_measurements.py rename to tests/lightning_qubit/test_measurements_class.py index 9ea81bdabb..eb5512b9c7 100644 --- a/tests/lightning_qubit2/test_measurements.py +++ b/tests/lightning_qubit/test_measurements_class.py @@ -30,8 +30,7 @@ from pennylane_lightning.lightning_qubit._measurements import LightningMeasurements -class CustomStateMeasurment(qml.measurements.StateMeasurement): - +class CustomStateMeasurement(qml.measurements.StateMeasurement): def process_state(self, state, wire_order): return 1 @@ -57,6 +56,7 @@ def test_initialization_complex128(): assert m.dtype == np.complex128 assert isinstance(m.measurement_lightning, MeasurementsC128) + class TestGetMeasurementFunction: """Tests for the get_measurement_function method.""" @@ -66,17 +66,21 @@ def test_only_support_state_measurements(self): statevector = LightningStateVector(num_wires=5, dtype=np.complex64) m = LightningMeasurements(statevector) - mp = qml.counts(wires=(0,1)) + mp = qml.counts(wires=(0, 1)) with pytest.raises(NotImplementedError): m.get_measurement_function(mp) - @pytest.mark.parametrize("mp", (qml.probs(wires=0), - qml.var(qml.Z(0)), - qml.vn_entropy(wires=0), - CustomStateMeasurment(), - qml.expval(qml.Identity(0)), - qml.expval(qml.Projector([1, 0], wires=(0,1))) - )) + @pytest.mark.parametrize( + "mp", + ( + qml.probs(wires=0), + qml.var(qml.Z(0)), + qml.vn_entropy(wires=0), + CustomStateMeasurement(), + qml.expval(qml.Identity(0)), + qml.expval(qml.Projector([1, 0], wires=(0, 1))), + ), + ) def test_state_diagonalizing_gates_measurements(self, mp): """Test that any non-expval measurement is""" statevector = LightningStateVector(num_wires=5, dtype=np.complex64) @@ -84,17 +88,20 @@ def test_state_diagonalizing_gates_measurements(self, mp): assert m.get_measurement_function(mp) == m.state_diagonalizing_gates - @pytest.mark.parametrize("obs", ( - qml.X(0), - qml.Y(0), - qml.Z(0), - qml.sum(qml.X(0), qml.Y(0)), - qml.prod(qml.X(0), qml.Y(1)), - qml.s_prod(2.0, qml.X(0)), - qml.Hamiltonian([1.0, 2.0], [qml.X(0), qml.Y(0)]), - qml.Hermitian(np.eye(2), wires=0), - qml.SparseHamiltonian(qml.X.compute_sparse_matrix(), wires=0), - )) + @pytest.mark.parametrize( + "obs", + ( + qml.X(0), + qml.Y(0), + qml.Z(0), + qml.sum(qml.X(0), qml.Y(0)), + qml.prod(qml.X(0), qml.Y(1)), + qml.s_prod(2.0, qml.X(0)), + qml.Hamiltonian([1.0, 2.0], [qml.X(0), qml.Y(0)]), + qml.Hermitian(np.eye(2), wires=0), + qml.SparseHamiltonian(qml.X.compute_sparse_matrix(), wires=0), + ), + ) def test_expval_selected(self, obs): """Test that expval is chosen for a variety of different expectation values.""" statevector = LightningStateVector(num_wires=5, dtype=np.complex64) @@ -126,7 +133,7 @@ def test_vn_entropy(self, method_name): """Test that state_diagonalizing_gates can handle an arbitrary measurement process.""" phi = 0.5 statevector = LightningStateVector(num_wires=5, dtype=np.complex64) - statevector.apply_operations([qml.IsingXX(phi, wires=(0,1))]) + statevector.apply_operations([qml.IsingXX(phi, wires=(0, 1))]) m = LightningMeasurements(statevector) measurement = qml.vn_entropy(wires=0) result = getattr(m, method_name)(measurement) @@ -136,10 +143,10 @@ def test_custom_measurement(self, method_name): """Test that LightningMeasurements can handle a custom state based measurement.""" statevector = LightningStateVector(num_wires=5, dtype=np.complex64) m = LightningMeasurements(statevector) - measurement = CustomStateMeasurment() + measurement = CustomStateMeasurement() result = getattr(m, method_name)(measurement) assert result == 1 - + def test_measurement_with_diagonalizing_gates(self, method_name): statevector = LightningStateVector(num_wires=5, dtype=np.complex64) m = LightningMeasurements(statevector) @@ -162,7 +169,7 @@ def test_basis_state_projector_expval(self, method_name): statevector.apply_operations([qml.RX(phi, 0)]) m = LightningMeasurements(statevector) result = getattr(m, method_name)(qml.expval(qml.Projector([0], wires=0))) - assert qml.math.allclose(result, np.cos(phi/2)**2) + assert qml.math.allclose(result, np.cos(phi / 2) ** 2) def test_state_vector_projector_expval(self, method_name): """Test expectation value for a state vector projector.""" @@ -171,5 +178,4 @@ def test_state_vector_projector_expval(self, method_name): statevector.apply_operations([qml.RX(phi, 0)]) m = LightningMeasurements(statevector) result = getattr(m, method_name)(qml.expval(qml.Projector([0, 1], wires=0))) - assert qml.math.allclose(result, np.sin(phi/2)**2) - + assert qml.math.allclose(result, np.sin(phi / 2) ** 2) diff --git a/tests/lightning_qubit/test_state_vector.py b/tests/lightning_qubit/test_state_vector_class.py similarity index 100% rename from tests/lightning_qubit/test_state_vector.py rename to tests/lightning_qubit/test_state_vector_class.py From 9aa3e359c84723bbebd7fc4de3382d2f45cad321 Mon Sep 17 00:00:00 2001 From: Dev version update bot Date: Fri, 23 Feb 2024 15:24:17 +0000 Subject: [PATCH 063/428] Auto update version --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index 92ab0f4480..1325db5058 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.35.0-dev17" +__version__ = "0.35.0-dev18" From b9fa21d8f4454c3092f09af45be8393b9a521158 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Fri, 23 Feb 2024 10:38:31 -0500 Subject: [PATCH 064/428] skip measurements class for other devices and in the absence of binaries --- tests/lightning_qubit/test_measurements_class.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/lightning_qubit/test_measurements_class.py b/tests/lightning_qubit/test_measurements_class.py index eb5512b9c7..751f006354 100644 --- a/tests/lightning_qubit/test_measurements_class.py +++ b/tests/lightning_qubit/test_measurements_class.py @@ -13,6 +13,7 @@ # limitations under the License. import pytest +from conftest import LightningDevice # tested device import numpy as np @@ -29,6 +30,13 @@ from pennylane_lightning.lightning_qubit._state_vector import LightningStateVector from pennylane_lightning.lightning_qubit._measurements import LightningMeasurements +from pennylane_lightning.lightning_qubit import LightningQubit + +if not LightningQubit._CPP_BINARY_AVAILABLE: + pytest.skip("No binary module found. Skipping.", allow_module_level=True) + +if LightningDevice != LightningQubit: + pytest.skip("Exclusive tests for lightning.qubit. Skipping.", allow_module_level=True) class CustomStateMeasurement(qml.measurements.StateMeasurement): def process_state(self, state, wire_order): From 48c67245a64eedd4951fac412de5de5d41baf537 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Fri, 23 Feb 2024 10:39:31 -0500 Subject: [PATCH 065/428] format --- tests/lightning_qubit/test_measurements_class.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/lightning_qubit/test_measurements_class.py b/tests/lightning_qubit/test_measurements_class.py index 751f006354..6ed4b2525b 100644 --- a/tests/lightning_qubit/test_measurements_class.py +++ b/tests/lightning_qubit/test_measurements_class.py @@ -38,6 +38,7 @@ if LightningDevice != LightningQubit: pytest.skip("Exclusive tests for lightning.qubit. Skipping.", allow_module_level=True) + class CustomStateMeasurement(qml.measurements.StateMeasurement): def process_state(self, state, wire_order): return 1 From 2a67020c653e15b36f5c86d38dea6b3d28ec4d22 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Fri, 23 Feb 2024 13:49:11 -0500 Subject: [PATCH 066/428] add LightningQubit2 to init and format --- pennylane_lightning/lightning_qubit/__init__.py | 1 + .../lightning_qubit/lightning_qubit2.py | 16 ++++++---------- tests/lightning_qubit2/test_expval_2.py | 6 +++--- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/__init__.py b/pennylane_lightning/lightning_qubit/__init__.py index 53e50cbf00..9d8ddab62d 100644 --- a/pennylane_lightning/lightning_qubit/__init__.py +++ b/pennylane_lightning/lightning_qubit/__init__.py @@ -15,3 +15,4 @@ from pennylane_lightning.core import __version__ from .lightning_qubit import LightningQubit +from .lightning_qubit2 import LightningQubit2 diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index f2e909d306..19d7c2e4dc 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -198,9 +198,7 @@ def accepted_observables(obs: qml.operation.Operator) -> bool: @simulator_tracking @single_tape_support class LightningQubit2(Device): - """PennyLane Lightning Qubit device. - - """ + """PennyLane Lightning Qubit device.""" _device_options = ("rng", "c_dtype", "batch_obs", "mcmc", "kernel_name", "num_burnin") @@ -217,9 +215,11 @@ def __init__( # pylint: disable=too-many-arguments batch_obs=False, ): if not LQ_CPP_BINARY_AVAILABLE: - raise ImportError("Pre-compiled binaries for lightning.qubit are not available. " + raise ImportError( + "Pre-compiled binaries for lightning.qubit are not available. " "To manually compile from source, follow the instructions at " - "https://pennylane-lightning.readthedocs.io/en/latest/installation.html.") + "https://pennylane-lightning.readthedocs.io/en/latest/installation.html." + ) super().__init__(wires=wires, shots=shots) self._statevector = LightningStateVector(num_wires=len(self.wires), dtype=c_dtype) @@ -298,9 +298,7 @@ def supports_derivatives( def preprocess(self, execution_config: ExecutionConfig = DefaultExecutionConfig): program = TransformProgram() - program.add_transform( - validate_measurements, name=self.name - ) + program.add_transform(validate_measurements, name=self.name) program.add_transform(no_sampling) program.add_transform(validate_observables, accepted_observables, name=self.name) program.add_transform(validate_device_wires, self.wires, name=self.name) @@ -314,7 +312,6 @@ def execute( circuits: QuantumTape_or_Batch, execution_config: ExecutionConfig = DefaultExecutionConfig, ) -> Result_or_ResultBatch: - results = [] for circuit in circuits: circuit = circuit.map_to_standard_wires() @@ -336,4 +333,3 @@ def execute_and_compute_derivatives( ): results = tuple(simulate_and_jacobian(c) for c in circuits) return tuple(zip(*results)) - diff --git a/tests/lightning_qubit2/test_expval_2.py b/tests/lightning_qubit2/test_expval_2.py index 17a1301538..1bf57af8a1 100644 --- a/tests/lightning_qubit2/test_expval_2.py +++ b/tests/lightning_qubit2/test_expval_2.py @@ -59,10 +59,10 @@ def test_Identity(self, theta, phi, c_dtype, tol): ops = [ qml.Identity(0), - qml.Identity((0,1)), - qml.Identity((1,2)), + qml.Identity((0, 1)), + qml.Identity((1, 2)), qml.RX(theta, 0), - qml.RX(phi, 1) + qml.RX(phi, 1), ] measurements = [qml.expval(qml.PauliZ(0))] tape = qml.tape.QuantumScript(ops, measurements) From a7166119bed60a6b00a1430f4c590e382b30c2e9 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Fri, 23 Feb 2024 15:01:41 -0500 Subject: [PATCH 067/428] update measurements class --- .../lightning_qubit/_measurements.py | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/_measurements.py b/pennylane_lightning/lightning_qubit/_measurements.py index 120a6696bb..520bd402a8 100644 --- a/pennylane_lightning/lightning_qubit/_measurements.py +++ b/pennylane_lightning/lightning_qubit/_measurements.py @@ -47,9 +47,24 @@ class LightningMeasurements: def __init__(self, qubit_state: LightningStateVector) -> None: self._qubit_state = qubit_state - self.state = qubit_state.state_vector - self.dtype = qubit_state.dtype - self.measurement_lightning = self._measurement_dtype()(self.state) + self._state = qubit_state.state_vector + self._dtype = qubit_state.dtype + self._measurement_lightning = self._measurement_dtype()(self.state) + + @property + def qubit_state(self): + """Returns a handle to the LightningStateVector class.""" + return self._qubit_state + + @property + def state(self): + """Returns a handle to the Lightning internal data class.""" + return self._state + + @property + def dtype(self): + """Returns the simulation data type.""" + return self._dtype def _measurement_dtype(self): """Binding to Lightning Managed state vector. @@ -94,7 +109,7 @@ def expval(self, measurementprocess: MeasurementProcess): CSR_SparseHamiltonian = measurementprocess.obs.sparse_matrix( wire_order=list(range(self._qubit_state.num_wires)) ).tocsr(copy=False) - return self.measurement_lightning.expval( + return self._measurement_lightning.expval( CSR_SparseHamiltonian.indptr, CSR_SparseHamiltonian.indices, CSR_SparseHamiltonian.data, @@ -106,11 +121,11 @@ def expval(self, measurementprocess: MeasurementProcess): or isinstance(measurementprocess.obs.name, List) ): ob_serialized = QuantumScriptSerializer( - self._qubit_state.name, self.dtype == np.complex64 + self._qubit_state.device_name, self.dtype == np.complex64 )._ob(measurementprocess.obs) - return self.measurement_lightning.expval(ob_serialized) + return self._measurement_lightning.expval(ob_serialized) - return self.measurement_lightning.expval( + return self._measurement_lightning.expval( measurementprocess.obs.name, measurementprocess.obs.wires ) From 261cb6d780ea85a0d7f40ffb106df971df969b85 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Fri, 23 Feb 2024 15:02:12 -0500 Subject: [PATCH 068/428] expand measurement class testing --- .../test_measurements_class.py | 368 +++++++++++++++--- 1 file changed, 317 insertions(+), 51 deletions(-) diff --git a/tests/lightning_qubit/test_measurements_class.py b/tests/lightning_qubit/test_measurements_class.py index 6ed4b2525b..52937ff643 100644 --- a/tests/lightning_qubit/test_measurements_class.py +++ b/tests/lightning_qubit/test_measurements_class.py @@ -16,8 +16,10 @@ from conftest import LightningDevice # tested device import numpy as np +import math import pennylane as qml +from pennylane.tape import QuantumScript try: from pennylane_lightning.lightning_qubit_ops import ( @@ -39,40 +41,40 @@ pytest.skip("Exclusive tests for lightning.qubit. Skipping.", allow_module_level=True) -class CustomStateMeasurement(qml.measurements.StateMeasurement): - def process_state(self, state, wire_order): - return 1 +# General LightningStateVector fixture, for any number of wires. +@pytest.fixture( + scope="function", + params=[np.complex64, np.complex128], +) +def lightning_sv(request): + def _statevector(num_wires): + return LightningStateVector(num_wires=num_wires, dtype=request.param) + return _statevector -def test_initialization_complex64(): - """Tests for the initialization of the LightningMeasurements class with np.complex64.""" - statevector = LightningStateVector(num_wires=5, dtype=np.complex64) - m = LightningMeasurements(statevector) - assert m._qubit_state is statevector - assert m.state is statevector.state_vector - assert m.dtype == np.complex64 - assert isinstance(m.measurement_lightning, MeasurementsC64) +class CustomStateMeasurement(qml.measurements.StateMeasurement): + def process_state(self, state, wire_order): + return 1 -def test_initialization_complex128(): +def test_initialization(lightning_sv): """Tests for the initialization of the LightningMeasurements class.""" - statevector = LightningStateVector(num_wires=5, dtype=np.complex128) + statevector = lightning_sv(num_wires=5) m = LightningMeasurements(statevector) - assert m._qubit_state is statevector + assert m.qubit_state is statevector assert m.state is statevector.state_vector - assert m.dtype == np.complex128 - assert isinstance(m.measurement_lightning, MeasurementsC128) + assert m.dtype == statevector.dtype class TestGetMeasurementFunction: """Tests for the get_measurement_function method.""" - def test_only_support_state_measurements(self): + def test_only_support_state_measurements(self, lightning_sv): """Test than a NotImplementedError is raised if the measurement is not a state measurement.""" - statevector = LightningStateVector(num_wires=5, dtype=np.complex64) + statevector = lightning_sv(num_wires=5) m = LightningMeasurements(statevector) mp = qml.counts(wires=(0, 1)) @@ -90,9 +92,9 @@ def test_only_support_state_measurements(self): qml.expval(qml.Projector([1, 0], wires=(0, 1))), ), ) - def test_state_diagonalizing_gates_measurements(self, mp): - """Test that any non-expval measurement is""" - statevector = LightningStateVector(num_wires=5, dtype=np.complex64) + def test_state_diagonalizing_gates_measurements(self, lightning_sv, mp): + """Test that any non-expval measurement calls the state_diagonalizing_gates method""" + statevector = lightning_sv(num_wires=5) m = LightningMeasurements(statevector) assert m.get_measurement_function(mp) == m.state_diagonalizing_gates @@ -111,80 +113,344 @@ def test_state_diagonalizing_gates_measurements(self, mp): qml.SparseHamiltonian(qml.X.compute_sparse_matrix(), wires=0), ), ) - def test_expval_selected(self, obs): + def test_expval_selected(self, lightning_sv, obs): """Test that expval is chosen for a variety of different expectation values.""" - statevector = LightningStateVector(num_wires=5, dtype=np.complex64) + statevector = lightning_sv(num_wires=5) m = LightningMeasurements(statevector) mp = qml.expval(obs) assert m.get_measurement_function(mp) == m.expval -def expected_entropy_ising_xx(param): - """ - Return the analytical entropy for the IsingXX. - """ - eig_1 = (1 + np.sqrt(1 - 4 * np.cos(param / 2) ** 2 * np.sin(param / 2) ** 2)) / 2 - eig_2 = (1 - np.sqrt(1 - 4 * np.cos(param / 2) ** 2 * np.sin(param / 2) ** 2)) / 2 - eigs = [eig_1, eig_2] - eigs = [eig for eig in eigs if eig > 0] - - expected_entropy = eigs * np.log(eigs) - - expected_entropy = -np.sum(expected_entropy) - return expected_entropy - - @pytest.mark.parametrize("method_name", ("state_diagonalizing_gates", "measurement")) class TestStateDiagonalizingGates: """Tests for various measurements that go through state_diagonalizing_gates""" - def test_vn_entropy(self, method_name): + def expected_entropy_Ising_XX(self, param): + """ + Return the analytical entropy for the IsingXX. + """ + eig_1 = (1 + np.sqrt(1 - 4 * np.cos(param / 2) ** 2 * np.sin(param / 2) ** 2)) / 2 + eig_2 = (1 - np.sqrt(1 - 4 * np.cos(param / 2) ** 2 * np.sin(param / 2) ** 2)) / 2 + eigs = [eig_1, eig_2] + eigs = [eig for eig in eigs if eig > 0] + + expected_entropy = eigs * np.log(eigs) + + expected_entropy = -np.sum(expected_entropy) + return expected_entropy + + def test_vn_entropy(self, lightning_sv, method_name): """Test that state_diagonalizing_gates can handle an arbitrary measurement process.""" phi = 0.5 - statevector = LightningStateVector(num_wires=5, dtype=np.complex64) + statevector = lightning_sv(num_wires=5) statevector.apply_operations([qml.IsingXX(phi, wires=(0, 1))]) m = LightningMeasurements(statevector) measurement = qml.vn_entropy(wires=0) result = getattr(m, method_name)(measurement) - assert qml.math.allclose(result, expected_entropy_ising_xx(phi)) + assert qml.math.allclose(result, self.expected_entropy_Ising_XX(phi)) - def test_custom_measurement(self, method_name): + def test_custom_measurement(self, lightning_sv, method_name): """Test that LightningMeasurements can handle a custom state based measurement.""" - statevector = LightningStateVector(num_wires=5, dtype=np.complex64) + statevector = lightning_sv(num_wires=5) m = LightningMeasurements(statevector) measurement = CustomStateMeasurement() result = getattr(m, method_name)(measurement) assert result == 1 - def test_measurement_with_diagonalizing_gates(self, method_name): - statevector = LightningStateVector(num_wires=5, dtype=np.complex64) + def test_measurement_with_diagonalizing_gates(self, lightning_sv, method_name): + statevector = lightning_sv(num_wires=5) m = LightningMeasurements(statevector) measurement = qml.probs(op=qml.X(0)) result = getattr(m, method_name)(measurement) assert qml.math.allclose(result, [0.5, 0.5]) - def test_identity_expval(self, method_name): + def test_identity_expval(self, lightning_sv, method_name): """Test that the expectation value of an identity is always one.""" - statevector = LightningStateVector(num_wires=5, dtype=np.complex64) + statevector = lightning_sv(num_wires=5) statevector.apply_operations([qml.Rot(0.5, 4.2, 6.8, wires=4)]) m = LightningMeasurements(statevector) result = getattr(m, method_name)(qml.expval(qml.I(4))) assert np.allclose(result, 1.0) - def test_basis_state_projector_expval(self, method_name): + def test_basis_state_projector_expval(self, lightning_sv, method_name): """Test expectation value for a basis state projector.""" phi = 0.8 - statevector = LightningStateVector(num_wires=1, dtype=np.complex64) + statevector = lightning_sv(num_wires=1) statevector.apply_operations([qml.RX(phi, 0)]) m = LightningMeasurements(statevector) result = getattr(m, method_name)(qml.expval(qml.Projector([0], wires=0))) assert qml.math.allclose(result, np.cos(phi / 2) ** 2) - def test_state_vector_projector_expval(self, method_name): + def test_state_vector_projector_expval(self, lightning_sv, method_name): """Test expectation value for a state vector projector.""" phi = -0.6 - statevector = LightningStateVector(num_wires=1, dtype=np.complex64) + statevector = lightning_sv(num_wires=1) statevector.apply_operations([qml.RX(phi, 0)]) m = LightningMeasurements(statevector) result = getattr(m, method_name)(qml.expval(qml.Projector([0, 1], wires=0))) assert qml.math.allclose(result, np.sin(phi / 2) ** 2) + + +@pytest.mark.parametrize("method_name", ("expval", "measurement")) +class TestExpval: + """Tests for the expval function""" + + wires = 2 + + @pytest.mark.parametrize( + "obs, expected", + [ + [qml.PauliX(0), -0.041892271271228736], + [qml.PauliX(1), 0.0], + [qml.PauliY(0), -0.5516350865364075], + [qml.PauliY(1), 0.0], + [qml.PauliZ(0), 0.8330328980789793], + [qml.PauliZ(1), 1.0], + ], + ) + def test_expval_qml_tape_wire0(self, obs, expected, tol, lightning_sv, method_name): + """Test expval with a circuit on wires=[0]""" + + x, y, z = [0.5, 0.3, -0.7] + statevector = lightning_sv(self.wires) + statevector.apply_operations( + [qml.RX(0.4, wires=[0]), qml.Rot(x, y, z, wires=[0]), qml.RY(-0.2, wires=[0])] + ) + + m = LightningMeasurements(statevector) + result = getattr(m, method_name)(qml.expval(obs)) + + assert np.allclose(result, expected, atol=tol, rtol=0) + + @pytest.mark.parametrize( + "obs, expected", + [ + [qml.PauliX(0), 0.0], + [qml.PauliX(1), -0.19866933079506122], + [qml.PauliY(0), -0.3894183423086505], + [qml.PauliY(1), 0.0], + [qml.PauliZ(0), 0.9210609940028852], + [qml.PauliZ(1), 0.9800665778412417], + ], + ) + def test_expval_wire01(self, obs, expected, tol, lightning_sv, method_name): + """Test expval with a circuit on wires=[0,1]""" + + statevector = lightning_sv(self.wires) + statevector.apply_operations([qml.RX(0.4, wires=[0]), qml.RY(-0.2, wires=[1])]) + + m = LightningMeasurements(statevector) + result = getattr(m, method_name)(qml.expval(obs)) + + assert np.allclose(result, expected, atol=tol, rtol=0) + + @pytest.mark.parametrize( + "obs, coeffs, expected", + [ + ([qml.PauliX(0) @ qml.PauliZ(1)], [1.0], 0.0), + ([qml.PauliZ(0) @ qml.PauliZ(1)], [1.0], math.cos(0.4) * math.cos(-0.2)), + ( + [ + qml.PauliX(0) @ qml.PauliZ(1), + qml.Hermitian( + [ + [1.0, 0.0, 0.0, 0.0], + [0.0, 3.0, 0.0, 0.0], + [0.0, 0.0, -1.0, 1.0], + [0.0, 0.0, 1.0, -2.0], + ], + wires=[0, 1], + ), + ], + [0.3, 1.0], + 0.9319728930156066, + ), + ], + ) + def test_expval_hamiltonian(self, obs, coeffs, expected, tol, lightning_sv, method_name): + """Test expval with Hamiltonian""" + ham = qml.Hamiltonian(coeffs, obs) + + statevector = lightning_sv(self.wires) + statevector.apply_operations([qml.RX(0.4, wires=[0]), qml.RY(-0.2, wires=[1])]) + + m = LightningMeasurements(statevector) + result = getattr(m, method_name)(qml.expval(ham)) + + assert np.allclose(result, expected, atol=tol, rtol=0) + + +# @pytest.mark.parametrize("operation,input,expected_output,par", test_data_two_wires_with_parameters) +# def measure_final_state(tol, operation, input, expected_output, par): +# """Tests that applying an operation yields the expected output state for two wire +# operations that have parameters.""" +# wires = 2 +# state_vector = LightningStateVector(wires) +# tape = QuantumScript( +# [qml.StatePrep(np.array(input), Wires(range(wires))), operation(*par, Wires(range(wires)))] +# ) +# final_state = state_vector.get_final_state(tape) + +# assert np.allclose(final_state.state, np.array(expected_output), atol=tol, rtol=0) +# assert final_state.state.dtype == final_state.dtype +# assert final_state == state_vector + + +THETA = np.linspace(0.11, 1, 3) +PHI = np.linspace(0.32, 1, 3) + + +@pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI))) +class TestExpval: + """Test expectation value calculations""" + + def test_Identity(self, theta, phi, tol, lightning_sv): + """Tests applying identities.""" + + wires = 3 + ops = [ + qml.Identity(0), + qml.Identity((0, 1)), + qml.Identity((1, 2)), + qml.RX(theta, 0), + qml.RX(phi, 1), + ] + measurements = [qml.expval(qml.PauliZ(0))] + tape = qml.tape.QuantumScript(ops, measurements) + + statevector = lightning_sv(wires) + statevector = statevector.get_final_state(tape) + m = LightningMeasurements(statevector) + result = m.measure_final_state(tape) + expected = np.cos(theta) + + assert np.allclose(result, expected, tol) + + def test_identity_expectation(self, theta, phi, tol, lightning_sv): + """Tests identity expectations.""" + + wires = 2 + tape = qml.tape.QuantumScript( + [qml.RX(theta, wires=[0]), qml.RX(phi, wires=[1]), qml.CNOT(wires=[0, 1])], + [qml.expval(qml.Identity(wires=[0])), qml.expval(qml.Identity(wires=[1]))], + ) + statevector = lightning_sv(wires) + statevector = statevector.get_final_state(tape) + m = LightningMeasurements(statevector) + result = m.measure_final_state(tape) + expected = 1.0 + + assert np.allclose(result, expected, tol) + + def test_multi_wire_identity_expectation(self, theta, phi, tol, lightning_sv): + """Tests multi-wire identity.""" + wires = 2 + tape = qml.tape.QuantumScript( + [qml.RX(theta, wires=[0]), qml.RX(phi, wires=[1]), qml.CNOT(wires=[0, 1])], + [qml.expval(qml.Identity(wires=[0, 1]))], + ) + statevector = lightning_sv(wires) + statevector = statevector.get_final_state(tape) + m = LightningMeasurements(statevector) + result = m.measure_final_state(tape) + expected = 1.0 + + assert np.allclose(result, expected, tol) + + @pytest.mark.parametrize( + "Obs, Op, expected_fn", + [ + ( + [qml.PauliX(wires=[0]), qml.PauliX(wires=[1])], + qml.RY, + lambda theta, phi: np.array([np.sin(theta) * np.sin(phi), np.sin(phi)]), + ), + ( + [qml.PauliY(wires=[0]), qml.PauliY(wires=[1])], + qml.RX, + lambda theta, phi: np.array([0, -np.cos(theta) * np.sin(phi)]), + ), + ( + [qml.PauliZ(wires=[0]), qml.PauliZ(wires=[1])], + qml.RX, + lambda theta, phi: np.array([np.cos(theta), np.cos(theta) * np.cos(phi)]), + ), + ( + [qml.Hadamard(wires=[0]), qml.Hadamard(wires=[1])], + qml.RY, + lambda theta, phi: np.array( + [ + np.sin(theta) * np.sin(phi) + np.cos(theta), + np.cos(theta) * np.cos(phi) + np.sin(phi), + ] + ) + / np.sqrt(2), + ), + ], + ) + def test_single_wire_observables_expectation( + self, Obs, Op, expected_fn, theta, phi, tol, lightning_sv + ): + """Test that expectation values for single wire observables is correct""" + wires = 3 + tape = qml.tape.QuantumScript( + [Op(theta, wires=[0]), Op(phi, wires=[1]), qml.CNOT(wires=[0, 1])], + [qml.expval(Obs[0]), qml.expval(Obs[1])], + ) + statevector = lightning_sv(wires) + statevector = statevector.get_final_state(tape) + m = LightningMeasurements(statevector) + result = m.measure_final_state(tape) + expected = expected_fn(theta, phi) + + assert np.allclose(result, expected, tol) + + +@pytest.mark.parametrize("phi", PHI) +class TestExpOperatorArithmetic: + """Test integration with SProd, Prod, and Sum.""" + + wires = 2 + + def test_sprod(self, phi, lightning_sv, tol): + """Test the `SProd` class.""" + tape = qml.tape.QuantumScript( + [qml.RX(phi, wires=0)], + [qml.expval(qml.s_prod(0.5, qml.PauliZ(0)))], + ) + statevector = lightning_sv(self.wires) + statevector = statevector.get_final_state(tape) + m = LightningMeasurements(statevector) + result = m.measure_final_state(tape) + expected = 0.5 * np.cos(phi) + + assert np.allclose(result, expected, tol) + + def test_prod(self, phi, lightning_sv, tol): + """Test the `Prod` class.""" + tape = qml.tape.QuantumScript( + [qml.RX(phi, wires=0), qml.Hadamard(1), qml.PauliZ(1)], + [qml.expval(qml.prod(qml.PauliZ(0), qml.PauliX(1)))], + ) + statevector = lightning_sv(self.wires) + statevector = statevector.get_final_state(tape) + m = LightningMeasurements(statevector) + result = m.measure_final_state(tape) + expected = -np.cos(phi) + + assert np.allclose(result, expected, tol) + + @pytest.mark.parametrize("theta", THETA) + def test_sum(self, phi, theta, lightning_sv, tol): + """Test the `Sum` class.""" + tape = qml.tape.QuantumScript( + [qml.RX(phi, wires=0), qml.RY(theta, wires=1)], + [qml.expval(qml.sum(qml.PauliZ(0), qml.PauliX(1)))], + ) + statevector = lightning_sv(self.wires) + statevector = statevector.get_final_state(tape) + m = LightningMeasurements(statevector) + result = m.measure_final_state(tape) + expected = np.cos(phi) + np.sin(theta) + + assert np.allclose(result, expected, tol) From aca8468697ea43de911ab87545acf4ca529733a5 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Fri, 23 Feb 2024 15:04:54 -0500 Subject: [PATCH 069/428] garbage collection --- tests/lightning_qubit/test_measurements_class.py | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/tests/lightning_qubit/test_measurements_class.py b/tests/lightning_qubit/test_measurements_class.py index 52937ff643..fa67cab7c7 100644 --- a/tests/lightning_qubit/test_measurements_class.py +++ b/tests/lightning_qubit/test_measurements_class.py @@ -280,22 +280,6 @@ def test_expval_hamiltonian(self, obs, coeffs, expected, tol, lightning_sv, meth assert np.allclose(result, expected, atol=tol, rtol=0) -# @pytest.mark.parametrize("operation,input,expected_output,par", test_data_two_wires_with_parameters) -# def measure_final_state(tol, operation, input, expected_output, par): -# """Tests that applying an operation yields the expected output state for two wire -# operations that have parameters.""" -# wires = 2 -# state_vector = LightningStateVector(wires) -# tape = QuantumScript( -# [qml.StatePrep(np.array(input), Wires(range(wires))), operation(*par, Wires(range(wires)))] -# ) -# final_state = state_vector.get_final_state(tape) - -# assert np.allclose(final_state.state, np.array(expected_output), atol=tol, rtol=0) -# assert final_state.state.dtype == final_state.dtype -# assert final_state == state_vector - - THETA = np.linspace(0.11, 1, 3) PHI = np.linspace(0.32, 1, 3) From 80e34942d107ff7cc5d9aac2b7acd2e3696248ef Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Fri, 23 Feb 2024 15:12:25 -0500 Subject: [PATCH 070/428] typo --- tests/lightning_qubit/test_measurements_class.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/lightning_qubit/test_measurements_class.py b/tests/lightning_qubit/test_measurements_class.py index fa67cab7c7..1e91c3f92e 100644 --- a/tests/lightning_qubit/test_measurements_class.py +++ b/tests/lightning_qubit/test_measurements_class.py @@ -375,7 +375,7 @@ def test_multi_wire_identity_expectation(self, theta, phi, tol, lightning_sv): def test_single_wire_observables_expectation( self, Obs, Op, expected_fn, theta, phi, tol, lightning_sv ): - """Test that expectation values for single wire observables is correct""" + """Test that expectation values for single wire observables are correct""" wires = 3 tape = qml.tape.QuantumScript( [Op(theta, wires=[0]), Op(phi, wires=[1]), qml.CNOT(wires=[0, 1])], From a284d7817cfa594b85c96d250ca9684cd5f72e80 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Mon, 26 Feb 2024 16:36:06 -0500 Subject: [PATCH 071/428] update coverage and StateVector class --- .../lightning_qubit/_state_vector.py | 5 ++++ .../test_state_vector_class.py | 25 +++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/pennylane_lightning/lightning_qubit/_state_vector.py b/pennylane_lightning/lightning_qubit/_state_vector.py index 0351f8277c..70e9bc1ee5 100644 --- a/pennylane_lightning/lightning_qubit/_state_vector.py +++ b/pennylane_lightning/lightning_qubit/_state_vector.py @@ -80,6 +80,11 @@ def wires(self): """All wires that can be addressed on this device""" return self._wires + @property + def num_wires(self): + """Number of wires addressed on this device""" + return self._num_wires + def _state_dtype(self): """Binding to Lightning Managed state vector. diff --git a/tests/lightning_qubit/test_state_vector_class.py b/tests/lightning_qubit/test_state_vector_class.py index b7ba9197ce..7ff1eef23e 100644 --- a/tests/lightning_qubit/test_state_vector_class.py +++ b/tests/lightning_qubit/test_state_vector_class.py @@ -90,6 +90,31 @@ def test_apply_operation_state_preparation(tol, operation, expected_output, par) assert np.allclose(state_vector.state, np.array(expected_output), atol=tol, rtol=0) +@pytest.mark.parametrize( + "operation,par", + [ + (qml.BasisState, [1, 0]), + (qml.QubitStateVector, [0, 0, 1, 0]), + ( + qml.StatePrep, + [1 / math.sqrt(3), 0, 1 / math.sqrt(3), 1 / math.sqrt(3)], + ), + ], +) +def test_reset_state(tol, operation, par): + """Tests that applying an operation yields the expected output state for single wire + operations that have no parameters.""" + + wires = 2 + state_vector = LightningStateVector(wires) + state_vector.apply_operations([operation(np.array(par), Wires(range(wires)))]) + + state_vector.reset_state() + + expected_output = state_vector._asarray([1, 0, 0, 0]) + assert np.allclose(state_vector.state, expected_output, atol=tol, rtol=0) + + test_data_no_parameters = [ (qml.PauliX, [1, 0], [0, 1]), (qml.PauliX, [1 / math.sqrt(2), 1 / math.sqrt(2)], [1 / math.sqrt(2), 1 / math.sqrt(2)]), From 43f72f60f3ea73eea3d6ce65ef8c186cb9d009ce Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Mon, 26 Feb 2024 16:36:52 -0500 Subject: [PATCH 072/428] expand measurements class coverage --- .../test_measurements_class.py | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/tests/lightning_qubit/test_measurements_class.py b/tests/lightning_qubit/test_measurements_class.py index 1e91c3f92e..e9fb6e482d 100644 --- a/tests/lightning_qubit/test_measurements_class.py +++ b/tests/lightning_qubit/test_measurements_class.py @@ -390,6 +390,43 @@ def test_single_wire_observables_expectation( assert np.allclose(result, expected, tol) +class TestSparseExpval: + """Tests for the expval function""" + + wires = 2 + + @pytest.mark.parametrize( + "ham_terms, expected", + [ + [qml.PauliX(0) @ qml.Identity(1), 0.00000000000000000], + [qml.Identity(0) @ qml.PauliX(1), -0.19866933079506122], + [qml.PauliY(0) @ qml.Identity(1), -0.38941834230865050], + [qml.Identity(0) @ qml.PauliY(1), 0.00000000000000000], + [qml.PauliZ(0) @ qml.Identity(1), 0.92106099400288520], + [qml.Identity(0) @ qml.PauliZ(1), 0.98006657784124170], + ], + ) + def test_sparse_Pauli_words(self, ham_terms, expected, tol, lightning_sv): + """Test expval of some simple sparse Hamiltonian""" + + ops = [qml.RX(0.4, wires=[0]), qml.RY(-0.2, wires=[1])] + measurements = [ + qml.expval( + qml.SparseHamiltonian( + qml.Hamiltonian([1], [ham_terms]).sparse_matrix(), wires=[0, 1] + ) + ) + ] + tape = qml.tape.QuantumScript(ops, measurements) + + statevector = lightning_sv(self.wires) + statevector = statevector.get_final_state(tape) + m = LightningMeasurements(statevector) + result = m.measure_final_state(tape) + + assert np.allclose(result, expected, tol) + + @pytest.mark.parametrize("phi", PHI) class TestExpOperatorArithmetic: """Test integration with SProd, Prod, and Sum.""" From 2a973d953444c2daa6f9e384b1a9bc197831f3ed Mon Sep 17 00:00:00 2001 From: Dev version update bot Date: Mon, 26 Feb 2024 21:37:18 +0000 Subject: [PATCH 073/428] Auto update version --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index 1325db5058..690571c1e5 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.35.0-dev18" +__version__ = "0.35.0-dev19" From f42f2980e0878246c6c7893387d0ab0460a2b744 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Tue, 27 Feb 2024 11:04:12 -0500 Subject: [PATCH 074/428] add coverage for n-controlled operations --- .../test_measurements_class.py | 191 +++++++++++++++++- 1 file changed, 190 insertions(+), 1 deletion(-) diff --git a/tests/lightning_qubit/test_measurements_class.py b/tests/lightning_qubit/test_measurements_class.py index e9fb6e482d..7e4a5ff01d 100644 --- a/tests/lightning_qubit/test_measurements_class.py +++ b/tests/lightning_qubit/test_measurements_class.py @@ -14,12 +14,13 @@ import pytest from conftest import LightningDevice # tested device +from pennylane.devices import DefaultQubit import numpy as np import math +import itertools import pennylane as qml -from pennylane.tape import QuantumScript try: from pennylane_lightning.lightning_qubit_ops import ( @@ -427,6 +428,194 @@ def test_sparse_Pauli_words(self, ham_terms, expected, tol, lightning_sv): assert np.allclose(result, expected, tol) +class TestControlledOps: + """Tests for controlled operations""" + + @staticmethod + def calculate_reference(tape): + dev = DefaultQubit(max_workers=1) + program, _ = dev.preprocess() + tapes, transf_fn = program([tape]) + results = dev.execute(tapes) + return transf_fn(results) + + @pytest.mark.parametrize( + "operation", + [ + qml.PauliX, + qml.PauliY, + qml.PauliZ, + qml.Hadamard, + qml.S, + qml.T, + qml.PhaseShift, + qml.RX, + qml.RY, + qml.RZ, + qml.Rot, + qml.SWAP, + qml.IsingXX, + qml.IsingXY, + qml.IsingYY, + qml.IsingZZ, + qml.SingleExcitation, + qml.SingleExcitationMinus, + qml.SingleExcitationPlus, + qml.DoubleExcitation, + qml.DoubleExcitationMinus, + qml.DoubleExcitationPlus, + qml.MultiRZ, + qml.GlobalPhase, + ], + ) + @pytest.mark.parametrize("control_value", [False, True]) + @pytest.mark.parametrize("n_qubits", list(range(2, 5))) + def test_controlled_qubit_gates(self, operation, n_qubits, control_value, tol, lightning_sv): + """Test that multi-controlled gates are correctly applied to a state""" + threshold = 250 + num_wires = max(operation.num_wires, 1) + + for n_wires in range(num_wires + 1, num_wires + 4): + wire_lists = list(itertools.permutations(range(0, n_qubits), n_wires)) + n_perms = len(wire_lists) * n_wires + if n_perms > threshold: + wire_lists = wire_lists[0 :: (n_perms // threshold)] + for all_wires in wire_lists: + target_wires = all_wires[0:num_wires] + control_wires = all_wires[num_wires:] + init_state = np.random.rand(2**n_qubits) + 1.0j * np.random.rand(2**n_qubits) + init_state /= np.sqrt(np.dot(np.conj(init_state), init_state)) + + ops = [ + qml.StatePrep(init_state, wires=range(n_qubits)), + ] + + if operation.num_params == 0: + ops += [ + qml.ctrl( + operation(target_wires), + control_wires, + control_values=[ + control_value or bool(i % 2) for i, _ in enumerate(control_wires) + ], + ), + ] + else: + ops += [ + qml.ctrl( + operation(*tuple([0.1234] * operation.num_params), target_wires), + control_wires, + control_values=[ + control_value or bool(i % 2) for i, _ in enumerate(control_wires) + ], + ), + ] + + measurements = [qml.state()] + tape = qml.tape.QuantumScript(ops, measurements) + + statevector = lightning_sv(n_qubits) + statevector = statevector.get_final_state(tape) + m = LightningMeasurements(statevector) + result = m.measure_final_state(tape) + expected = self.calculate_reference(tape) + + assert np.allclose(result, expected, tol) + + def test_controlled_qubit_unitary_from_op(self, tol, lightning_sv): + n_qubits = 10 + par = 0.1234 + + tape = qml.tape.QuantumScript( + [ + qml.ControlledQubitUnitary( + qml.QubitUnitary(qml.RX.compute_matrix(par), wires=5), control_wires=range(5) + ) + ], + [qml.expval(qml.PauliX(0))], + ) + + statevector = lightning_sv(n_qubits) + statevector = statevector.get_final_state(tape) + m = LightningMeasurements(statevector) + result = m.measure_final_state(tape) + expected = self.calculate_reference(tape) + + assert np.allclose(result, expected, tol) + + @pytest.mark.parametrize("control_wires", range(4)) + @pytest.mark.parametrize("target_wires", range(4)) + def test_cnot_controlled_qubit_unitary(self, control_wires, target_wires, tol, lightning_sv): + """Test that ControlledQubitUnitary is correctly applied to a state""" + if control_wires == target_wires: + return + n_qubits = 4 + control_wires = [control_wires] + target_wires = [target_wires] + wires = control_wires + target_wires + U = qml.matrix(qml.PauliX(target_wires)) + init_state = np.random.rand(2**n_qubits) + 1.0j * np.random.rand(2**n_qubits) + init_state /= np.sqrt(np.dot(np.conj(init_state), init_state)) + + tape = qml.tape.QuantumScript( + [ + qml.StatePrep(init_state, wires=range(n_qubits)), + qml.ControlledQubitUnitary(U, control_wires=control_wires, wires=target_wires), + ], + [qml.state()], + ) + tape_cnot = qml.tape.QuantumScript( + [qml.StatePrep(init_state, wires=range(n_qubits)), qml.CNOT(wires=wires)], [qml.state()] + ) + + statevector = lightning_sv(n_qubits) + statevector = statevector.get_final_state(tape) + m = LightningMeasurements(statevector) + result = m.measure_final_state(tape) + expected = self.calculate_reference(tape_cnot) + + assert np.allclose(result, expected, tol) + + @pytest.mark.parametrize("control_value", [False, True]) + @pytest.mark.parametrize("n_qubits", list(range(2, 8))) + def test_controlled_globalphase(self, n_qubits, control_value, tol, lightning_sv): + """Test that multi-controlled gates are correctly applied to a state""" + threshold = 250 + operation = qml.GlobalPhase + num_wires = max(operation.num_wires, 1) + for n_wires in range(num_wires + 1, num_wires + 4): + wire_lists = list(itertools.permutations(range(0, n_qubits), n_wires)) + n_perms = len(wire_lists) * n_wires + if n_perms > threshold: + wire_lists = wire_lists[0 :: (n_perms // threshold)] + for all_wires in wire_lists: + target_wires = all_wires[0:num_wires] + control_wires = all_wires[num_wires:] + init_state = np.random.rand(2**n_qubits) + 1.0j * np.random.rand(2**n_qubits) + init_state /= np.sqrt(np.dot(np.conj(init_state), init_state)) + + tape = qml.tape.QuantumScript( + [ + qml.StatePrep(init_state, wires=range(n_qubits)), + qml.ctrl( + operation(0.1234, target_wires), + control_wires, + control_values=[ + control_value or bool(i % 2) for i, _ in enumerate(control_wires) + ], + ), + ], + [qml.state()], + ) + statevector = lightning_sv(n_qubits) + statevector = statevector.get_final_state(tape) + m = LightningMeasurements(statevector) + result = m.measure_final_state(tape) + expected = self.calculate_reference(tape) + + assert np.allclose(result, expected, tol) + + @pytest.mark.parametrize("phi", PHI) class TestExpOperatorArithmetic: """Test integration with SProd, Prod, and Sum.""" From 9cd54710d9cce2cc6d52a2c9bf7debf24b5dc0cb Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Tue, 27 Feb 2024 11:05:25 -0500 Subject: [PATCH 075/428] add map to standard wires to get_final_state for safety --- pennylane_lightning/lightning_qubit/_state_vector.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pennylane_lightning/lightning_qubit/_state_vector.py b/pennylane_lightning/lightning_qubit/_state_vector.py index 70e9bc1ee5..8084e981d8 100644 --- a/pennylane_lightning/lightning_qubit/_state_vector.py +++ b/pennylane_lightning/lightning_qubit/_state_vector.py @@ -353,6 +353,7 @@ def get_final_state(self, circuit: QuantumScript): LightningStateVector: Lightning final state class. """ + circuit = circuit.map_to_standard_wires() self.apply_operations(circuit.operations) return self From cd3096c8e6799d8cd57a1b545a179a449205e290 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Tue, 27 Feb 2024 11:19:26 -0500 Subject: [PATCH 076/428] update jax config import --- mpitests/test_adjoint_jacobian.py | 2 +- tests/test_adjoint_jacobian.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mpitests/test_adjoint_jacobian.py b/mpitests/test_adjoint_jacobian.py index 0c7407bb27..8d6cc8e219 100644 --- a/mpitests/test_adjoint_jacobian.py +++ b/mpitests/test_adjoint_jacobian.py @@ -872,7 +872,7 @@ def test_interface_jax(self, dev): jax = pytest.importorskip("jax") if dev.R_DTYPE == np.float64: - from jax.config import config # pylint: disable=import-outside-toplevel + from jax import config # pylint: disable=import-outside-toplevel config.update("jax_enable_x64", True) diff --git a/tests/test_adjoint_jacobian.py b/tests/test_adjoint_jacobian.py index eaf6aa59eb..871a39e338 100644 --- a/tests/test_adjoint_jacobian.py +++ b/tests/test_adjoint_jacobian.py @@ -998,7 +998,7 @@ def test_interface_jax(self, dev): jax = pytest.importorskip("jax") if dev.R_DTYPE == np.float64: - from jax.config import config + from jax import config config.update("jax_enable_x64", True) From 98db9dbe10bff5604b07aea3ec5f1baec78dcdef Mon Sep 17 00:00:00 2001 From: Dev version update bot Date: Tue, 27 Feb 2024 16:55:15 +0000 Subject: [PATCH 077/428] Auto update version --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index 690571c1e5..072fcd69e0 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.35.0-dev19" +__version__ = "0.35.0-dev20" From 4f746d0f5afbeabd16845a48b84753a1c57105c2 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Tue, 27 Feb 2024 11:57:08 -0500 Subject: [PATCH 078/428] trigger CI From 03959455ec6dd39cba1f9daf7ef52b5f7d9b02a5 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Wed, 28 Feb 2024 12:35:39 -0500 Subject: [PATCH 079/428] update state vector class and tests for improved coverage --- .../lightning_qubit/_state_vector.py | 7 ++--- .../test_state_vector_class.py | 28 +++++++++++++++++++ 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/_state_vector.py b/pennylane_lightning/lightning_qubit/_state_vector.py index 8084e981d8..3e041f34e1 100644 --- a/pennylane_lightning/lightning_qubit/_state_vector.py +++ b/pennylane_lightning/lightning_qubit/_state_vector.py @@ -218,7 +218,7 @@ def _get_basis_state_index(self, state, wires): basis_states = qml.math.convert_like(basis_states, state) return int(qml.math.dot(state, basis_states)) - def _apply_state_vector(self, state, device_wires): + def _apply_state_vector(self, state, device_wires: Wires): """Initialize the internal state vector in a specified state. Args: state (array[complex]): normalized input state of length ``2**len(wires)`` @@ -228,7 +228,7 @@ def _apply_state_vector(self, state, device_wires): if isinstance(state, self._qubit_state.__class__): state_data = allocate_aligned_array(state.size, np.dtype(self.dtype), True) - self._qubit_state.getState(state_data) + state.getState(state_data) state = state_data ravelled_indices, state = self._preprocess_state_vector(state, device_wires) @@ -270,8 +270,6 @@ def _apply_lightning_controlled(self, operation): state = self.state_vector basename = operation.base.name - if basename == "Identity": - return method = getattr(state, f"{basename}", None) control_wires = list(operation.control_wires) control_values = operation.control_values @@ -353,7 +351,6 @@ def get_final_state(self, circuit: QuantumScript): LightningStateVector: Lightning final state class. """ - circuit = circuit.map_to_standard_wires() self.apply_operations(circuit.operations) return self diff --git a/tests/lightning_qubit/test_state_vector_class.py b/tests/lightning_qubit/test_state_vector_class.py index 7ff1eef23e..b1c4e3550d 100644 --- a/tests/lightning_qubit/test_state_vector_class.py +++ b/tests/lightning_qubit/test_state_vector_class.py @@ -58,6 +58,34 @@ def test_wrong_dtype(dtype): assert LightningStateVector(3, dtype=dtype) +@pytest.mark.parametrize("dtype", [np.double, np.complex64, None]) +@pytest.mark.parametrize("data", [1.0, [1.0], [1.0, 2.0]]) +def test_asarray(dtype, data, tol): + """Test _asarray returns the right values""" + wires = 2 + state_vector = LightningStateVector(wires) + assert np.allclose(data, state_vector._asarray(data, dtype), atol=tol) + + +def test_errors_basis_state(): + with pytest.raises(ValueError, match="BasisState parameter must consist of 0 or 1 integers."): + state_vector = LightningStateVector(2) + state_vector.apply_operations([qml.BasisState(np.array([-0.2, 4.2]), wires=[0, 1])]) + with pytest.raises(ValueError, match="BasisState parameter and wires must be of equal length."): + state_vector = LightningStateVector(1) + state_vector.apply_operations([qml.BasisState(np.array([0, 1]), wires=[0])]) + + +def test_apply_state_vector_with_lightning_handle(tol): + state_vector_1 = LightningStateVector(2) + state_vector_1.apply_operations([qml.BasisState(np.array([0, 1]), wires=[0, 1])]) + + state_vector_2 = LightningStateVector(2) + state_vector_2._apply_state_vector(state_vector_1.state_vector, Wires([0, 1])) + + assert np.allclose(state_vector_1.state, state_vector_2.state, atol=tol, rtol=0) + + @pytest.mark.parametrize( "operation,expected_output,par", [ From 4a863ba332a31a7b63bb24fcb84d4bfd4cf22cbf Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Wed, 28 Feb 2024 12:36:04 -0500 Subject: [PATCH 080/428] update measurement class tests --- .../test_measurements_class.py | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/lightning_qubit/test_measurements_class.py b/tests/lightning_qubit/test_measurements_class.py index 7e4a5ff01d..dcb497b7c5 100644 --- a/tests/lightning_qubit/test_measurements_class.py +++ b/tests/lightning_qubit/test_measurements_class.py @@ -664,3 +664,29 @@ def test_sum(self, phi, theta, lightning_sv, tol): expected = np.cos(phi) + np.sin(theta) assert np.allclose(result, expected, tol) + + +@pytest.mark.parametrize( + "op,par,wires,expected", + [ + (qml.QubitStateVector, [0, 1], [1], [1, -1]), + (qml.QubitStateVector, [0, 1], [0], [-1, 1]), + (qml.QubitStateVector, [1.0 / np.sqrt(2), 1.0 / np.sqrt(2)], [1], [1, 0]), + (qml.QubitStateVector, [1j / 2.0, np.sqrt(3) / 2.0], [1], [1, -0.5]), + (qml.QubitStateVector, [(2 - 1j) / 3.0, 2j / 3.0], [0], [1 / 9.0, 1]), + ], +) +def test_state_vector_2_qubit_subset(tol, op, par, wires, expected, lightning_sv): + """Tests qubit state vector preparation and measure on subsets of 2 qubits""" + + tape = qml.tape.QuantumScript( + [op(par, wires=wires)], [qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliZ(1))] + ) + + statevector = lightning_sv(2) + statevector = statevector.get_final_state(tape) + + m = LightningMeasurements(statevector) + result = m.measure_final_state(tape) + + assert np.allclose(result, expected, tol) From 4b44e4f78a5d0b3ef321d0441c26836b5ddec421 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Wed, 28 Feb 2024 13:30:51 -0500 Subject: [PATCH 081/428] update dev version --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index 072fcd69e0..b720300480 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.35.0-dev20" +__version__ = "0.35.0-dev21" From 3a87ca8708d94c4bad8f0164d136935457e4176a Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Wed, 28 Feb 2024 13:53:38 -0500 Subject: [PATCH 082/428] add cpp binary available variable --- pennylane_lightning/lightning_qubit/lightning_qubit2.py | 5 ++++- tests/lightning_qubit2/test_expval_2.py | 6 +++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index 00316320df..342a9d816f 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -211,7 +211,10 @@ def __init__( # pylint: disable=too-many-arguments num_burnin=100, batch_obs=False, ): - if not LQ_CPP_BINARY_AVAILABLE: + if LQ_CPP_BINARY_AVAILABLE: + self._CPP_BINARY_AVAILABLE = True + else: + self._CPP_BINARY_AVAILABLE = False raise ImportError( "Pre-compiled binaries for lightning.qubit are not available. " "To manually compile from source, follow the instructions at " diff --git a/tests/lightning_qubit2/test_expval_2.py b/tests/lightning_qubit2/test_expval_2.py index 1bf57af8a1..abdaf05a6a 100644 --- a/tests/lightning_qubit2/test_expval_2.py +++ b/tests/lightning_qubit2/test_expval_2.py @@ -23,12 +23,12 @@ from conftest import LightningDevice # tested device -if not LightningQubit._CPP_BINARY_AVAILABLE: - pytest.skip("No binary module found. Skipping.", allow_module_level=True) - if LightningDevice != LightningQubit: pytest.skip("Exclusive tests for lightning.qubit. Skipping.", allow_module_level=True) +if not LightningQubit._CPP_BINARY_AVAILABLE: + pytest.skip("No binary module found. Skipping.", allow_module_level=True) + THETA = np.linspace(0.11, 1, 3) PHI = np.linspace(0.32, 1, 3) VARPHI = np.linspace(0.02, 1, 3) From 62c43f0f9c150ecf036848a27686d5790bfef7c1 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Wed, 28 Feb 2024 14:13:22 -0500 Subject: [PATCH 083/428] remove device definition --- .../lightning_qubit/lightning_qubit2.py | 59 ------------------- 1 file changed, 59 deletions(-) delete mode 100644 pennylane_lightning/lightning_qubit/lightning_qubit2.py diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py deleted file mode 100644 index bbdc799400..0000000000 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright 2018-2024 Xanadu Quantum Technologies Inc. - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -""" -This module contains the LightningQubit2 class that inherits from the new device interface. - -""" -import numpy as np - -from pennylane.tape import QuantumScript -from pennylane.typing import Result - -from ._state_vector import LightningStateVector -from ._measurements import LightningMeasurements - - -try: - # pylint: disable=import-error, unused-import - import pennylane_lightning.lightning_qubit_ops - - LQ_CPP_BINARY_AVAILABLE = True -except ImportError: - LQ_CPP_BINARY_AVAILABLE = False - - -def simulate(circuit: QuantumScript, state: LightningStateVector) -> Result: - """Simulate a single quantum script. - - Args: - circuit (QuantumTape): The single circuit to simulate - state (LightningStateVector): handle to Lightning state vector - - Returns: - tuple(TensorLike): The results of the simulation - - Note that this function can return measurements for non-commuting observables simultaneously. - - """ - state.reset_state() - final_state = state.get_final_state(circuit) - return LightningMeasurements(final_state).measure_final_state(circuit) - - -def dummy_jacobian(circuit: QuantumScript): - return np.array(0.0) - - -def simulate_and_jacobian(circuit: QuantumScript): - return np.array(0.0), np.array(0.0) From 2d262888d9da7baee512ed9b015275a63430c3f1 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Wed, 28 Feb 2024 15:04:22 -0500 Subject: [PATCH 084/428] update dev version --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index b720300480..60da66632b 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.35.0-dev21" +__version__ = "0.35.0-dev22" From 3427562aab4350ef4310b5bf783e81764e47cbaf Mon Sep 17 00:00:00 2001 From: Dev version update bot Date: Wed, 28 Feb 2024 20:08:10 +0000 Subject: [PATCH 085/428] Auto update version --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index b720300480..60da66632b 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.35.0-dev21" +__version__ = "0.35.0-dev22" From dcfb1904242d6532b53bb18390e608cb0f6e4adb Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Thu, 29 Feb 2024 08:29:56 -0500 Subject: [PATCH 086/428] reduce dependency on DefaultQubit for tests --- tests/lightning_qubit2/test_expval_2.py | 120 +++++++++++------------- 1 file changed, 56 insertions(+), 64 deletions(-) diff --git a/tests/lightning_qubit2/test_expval_2.py b/tests/lightning_qubit2/test_expval_2.py index abdaf05a6a..1349e02b94 100644 --- a/tests/lightning_qubit2/test_expval_2.py +++ b/tests/lightning_qubit2/test_expval_2.py @@ -18,7 +18,7 @@ import numpy as np import pennylane as qml -from pennylane_lightning.lightning_qubit import LightningQubit2 as LightningQubit +from pennylane_lightning.lightning_qubit import LightningQubit, LightningQubit2 from pennylane.devices import DefaultQubit from conftest import LightningDevice # tested device @@ -26,7 +26,7 @@ if LightningDevice != LightningQubit: pytest.skip("Exclusive tests for lightning.qubit. Skipping.", allow_module_level=True) -if not LightningQubit._CPP_BINARY_AVAILABLE: +if not LightningQubit2._CPP_BINARY_AVAILABLE: pytest.skip("No binary module found. Skipping.", allow_module_level=True) THETA = np.linspace(0.11, 1, 3) @@ -34,11 +34,14 @@ VARPHI = np.linspace(0.02, 1, 3) -@pytest.mark.parametrize("c_dtype", (np.complex64, np.complex128)) @pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI))) class TestExpval: """Test expectation value calculations""" + @pytest.fixture(params=[np.complex64, np.complex128]) + def dev(self, request): + return LightningQubit2(wires=3, c_dtype=request.param) + @staticmethod def calculate_reference(tape): dev = DefaultQubit(max_workers=1) @@ -54,7 +57,7 @@ def process_and_execute(dev, tape): results = dev.execute(tapes) return transf_fn(results) - def test_Identity(self, theta, phi, c_dtype, tol): + def test_Identity(self, theta, phi, dev, tol): """Tests applying identities.""" ops = [ @@ -67,17 +70,15 @@ def test_Identity(self, theta, phi, c_dtype, tol): measurements = [qml.expval(qml.PauliZ(0))] tape = qml.tape.QuantumScript(ops, measurements) - dev = LightningQubit(c_dtype=c_dtype, wires=3) result = dev.execute(tape) expected = np.cos(theta) tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 assert np.allclose(result, expected, tol) - def test_identity_expectation(self, theta, phi, c_dtype, tol): + def test_identity_expectation(self, theta, phi, dev, tol): """Tests identity expectations.""" - dev = LightningQubit(wires=2, c_dtype=c_dtype) tape = qml.tape.QuantumScript( [qml.RX(theta, wires=[0]), qml.RX(phi, wires=[1]), qml.CNOT(wires=[0, 1])], [qml.expval(qml.Identity(wires=[0])), qml.expval(qml.Identity(wires=[1]))], @@ -88,10 +89,9 @@ def test_identity_expectation(self, theta, phi, c_dtype, tol): assert np.allclose(1.0, result, tol) - def test_multi_wire_identity_expectation(self, theta, phi, c_dtype, tol): + def test_multi_wire_identity_expectation(self, theta, phi, dev, tol): """Tests multi-wire identity.""" - dev = LightningQubit(wires=2, c_dtype=c_dtype) tape = qml.tape.QuantumScript( [qml.RX(theta, wires=[0]), qml.RX(phi, wires=[1]), qml.CNOT(wires=[0, 1])], [qml.expval(qml.Identity(wires=[0, 1]))], @@ -109,68 +109,66 @@ def test_multi_wire_identity_expectation(self, theta, phi, c_dtype, tol): (["b", "a"]), ], ) - def test_PauliZ_expectation(self, theta, phi, c_dtype, tol, wires): - """Tests PauliZ.""" + def test_custom_wires(self, theta, phi, tol, wires): + """Tests custom wires.""" + dev = LightningQubit2(wires=wires) - dev = LightningQubit(wires=wires, c_dtype=c_dtype) tape = qml.tape.QuantumScript( [qml.RX(theta, wires=wires[0]), qml.RX(phi, wires=wires[1]), qml.CNOT(wires=wires)], [qml.expval(qml.PauliZ(wires=wires[0])), qml.expval(qml.PauliZ(wires=wires[1]))], ) calculated_val = self.process_and_execute(dev, tape) - reference_val = self.calculate_reference(tape) - - tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 - - assert np.allclose(calculated_val, reference_val, tol) - - def test_PauliX_expectation(self, theta, phi, dev, tol): - """Tests PauliX.""" - - tape = qml.tape.QuantumScript( - [qml.RY(theta, wires=[0]), qml.RY(phi, wires=[1]), qml.CNOT(wires=[0, 1])], - [qml.expval(qml.PauliX(wires=[0])), qml.expval(qml.PauliX(wires=[1]))], - ) - - calculated_val = self.process_and_execute(dev, tape) - reference_val = self.calculate_reference(tape) - - tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 - - assert np.allclose(calculated_val, reference_val, tol) - - def test_PauliY_expectation(self, theta, phi, dev, tol): - """Tests PauliY.""" - - tape = qml.tape.QuantumScript( - [qml.RX(theta, wires=[0]), qml.RX(phi, wires=[1]), qml.CNOT(wires=[0, 1])], - [qml.expval(qml.PauliY(wires=[0])), qml.expval(qml.PauliY(wires=[1]))], - ) - - calculated_val = self.process_and_execute(dev, tape) - reference_val = self.calculate_reference(tape) + reference_val = np.array([np.cos(theta), np.cos(theta) * np.cos(phi)]) tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 assert np.allclose(calculated_val, reference_val, tol) - def test_hadamard_expectation(self, theta, phi, dev, tol): - """Tests Hadamard.""" + @pytest.mark.parametrize( + "Obs, Op, expected_fn", + [ + ( + [qml.PauliX(wires=[0]), qml.PauliX(wires=[1])], + qml.RY, + lambda theta, phi: np.array([np.sin(theta) * np.sin(phi), np.sin(phi)]), + ), + ( + [qml.PauliY(wires=[0]), qml.PauliY(wires=[1])], + qml.RX, + lambda theta, phi: np.array([0, -np.cos(theta) * np.sin(phi)]), + ), + ( + [qml.PauliZ(wires=[0]), qml.PauliZ(wires=[1])], + qml.RX, + lambda theta, phi: np.array([np.cos(theta), np.cos(theta) * np.cos(phi)]), + ), + ( + [qml.Hadamard(wires=[0]), qml.Hadamard(wires=[1])], + qml.RY, + lambda theta, phi: np.array( + [ + np.sin(theta) * np.sin(phi) + np.cos(theta), + np.cos(theta) * np.cos(phi) + np.sin(phi), + ] + ) + / np.sqrt(2), + ), + ], + ) + def test_single_wire_observables_expectation(self, Obs, Op, expected_fn, theta, phi, tol, dev): + """Test that expectation values for single wire observables are correct""" tape = qml.tape.QuantumScript( - [qml.RY(theta, wires=[0]), qml.RY(phi, wires=[1]), qml.CNOT(wires=[0, 1])], - [qml.expval(qml.Hadamard(wires=[0])), qml.expval(qml.Hadamard(wires=[1]))], + [Op(theta, wires=[0]), Op(phi, wires=[1]), qml.CNOT(wires=[0, 1])], + [qml.expval(Obs[0]), qml.expval(Obs[1])], ) + result = self.process_and_execute(dev, tape) + expected = expected_fn(theta, phi) - calculated_val = self.process_and_execute(dev, tape) - reference_val = self.calculate_reference(tape) - - tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 - - assert np.allclose(calculated_val, reference_val, tol) + assert np.allclose(result, expected, tol) - def test_hermitian_expectation(self, theta, phi, dev, tol): + def test_hermitian_expectation(self, theta, phi, tol, dev): """Tests an Hermitian operator.""" with qml.tape.QuantumTape() as tape: @@ -184,11 +182,9 @@ def test_hermitian_expectation(self, theta, phi, dev, tol): calculated_val = self.process_and_execute(dev, tape) reference_val = self.calculate_reference(tape) - tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 - assert np.allclose(calculated_val, reference_val, tol) - def test_hamiltonian_expectation(self, theta, phi, dev, tol): + def test_hamiltonian_expectation(self, theta, phi, tol, dev): """Tests a Hamiltonian.""" ham = qml.Hamiltonian( @@ -211,11 +207,9 @@ def test_hamiltonian_expectation(self, theta, phi, dev, tol): calculated_val = self.process_and_execute(dev, tape) reference_val = self.calculate_reference(tape) - tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 - assert np.allclose(calculated_val, reference_val, tol) - def test_sparse_hamiltonian_expectation(self, theta, phi, dev, tol): + def test_sparse_hamiltonian_expectation(self, theta, phi, tol, dev): """Tests a Hamiltonian.""" ham = qml.SparseHamiltonian( @@ -240,8 +234,6 @@ def test_sparse_hamiltonian_expectation(self, theta, phi, dev, tol): calculated_val = self.process_and_execute(dev, tape) reference_val = self.calculate_reference(tape) - tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 - assert np.allclose(calculated_val, reference_val, tol) @@ -251,7 +243,7 @@ class TestOperatorArithmetic: @pytest.fixture(params=[np.complex64, np.complex128]) def dev(self, request): - return LightningQubit(c_dtype=request.param) + return LightningQubit2(wires=2, c_dtype=request.param) @staticmethod def calculate_reference(tape): @@ -336,7 +328,7 @@ class TestTensorExpval: @pytest.fixture(params=[np.complex64, np.complex128]) def dev(self, request): - return LightningQubit(c_dtype=request.param) + return LightningQubit2(wires=3, c_dtype=request.param) @staticmethod def calculate_reference(tape): From 6660818c7a8bd554952fa00d0d9cec73f19224ac Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Thu, 29 Feb 2024 08:31:43 -0500 Subject: [PATCH 087/428] update LightningQubit2 --- pennylane_lightning/lightning_qubit/lightning_qubit2.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index 342a9d816f..03fcde440c 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -199,6 +199,8 @@ class LightningQubit2(Device): _device_options = ("rng", "c_dtype", "batch_obs", "mcmc", "kernel_name", "num_burnin") + _CPP_BINARY_AVAILABLE = LQ_CPP_BINARY_AVAILABLE + def __init__( # pylint: disable=too-many-arguments self, wires, @@ -211,15 +213,13 @@ def __init__( # pylint: disable=too-many-arguments num_burnin=100, batch_obs=False, ): - if LQ_CPP_BINARY_AVAILABLE: - self._CPP_BINARY_AVAILABLE = True - else: - self._CPP_BINARY_AVAILABLE = False + if not LQ_CPP_BINARY_AVAILABLE: raise ImportError( "Pre-compiled binaries for lightning.qubit are not available. " "To manually compile from source, follow the instructions at " "https://pennylane-lightning.readthedocs.io/en/latest/installation.html." ) + super().__init__(wires=wires, shots=shots) self._statevector = LightningStateVector(num_wires=len(self.wires), dtype=c_dtype) From 8a9b9a5bb18f685cf8a5f94e9edc93296b8b6b87 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Thu, 29 Feb 2024 10:47:48 -0500 Subject: [PATCH 088/428] clean test_measurements_class.py --- .../test_measurements_class.py | 156 ++++++------------ 1 file changed, 52 insertions(+), 104 deletions(-) diff --git a/tests/lightning_qubit/test_measurements_class.py b/tests/lightning_qubit/test_measurements_class.py index dcb497b7c5..a9124901eb 100644 --- a/tests/lightning_qubit/test_measurements_class.py +++ b/tests/lightning_qubit/test_measurements_class.py @@ -12,28 +12,23 @@ # See the License for the specific language governing permissions and # limitations under the License. -import pytest -from conftest import LightningDevice # tested device -from pennylane.devices import DefaultQubit - -import numpy as np -import math import itertools +import math +import numpy as np import pennylane as qml +import pytest +from conftest import LightningDevice # tested device +from pennylane.devices import DefaultQubit try: - from pennylane_lightning.lightning_qubit_ops import ( - MeasurementsC64, - MeasurementsC128, - ) + from pennylane_lightning.lightning_qubit_ops import MeasurementsC64, MeasurementsC128 except ImportError: pass -from pennylane_lightning.lightning_qubit._state_vector import LightningStateVector -from pennylane_lightning.lightning_qubit._measurements import LightningMeasurements - from pennylane_lightning.lightning_qubit import LightningQubit +from pennylane_lightning.lightning_qubit._measurements import LightningMeasurements +from pennylane_lightning.lightning_qubit._state_vector import LightningStateVector if not LightningQubit._CPP_BINARY_AVAILABLE: pytest.skip("No binary module found. Skipping.", allow_module_level=True) @@ -192,104 +187,15 @@ def test_state_vector_projector_expval(self, lightning_sv, method_name): assert qml.math.allclose(result, np.sin(phi / 2) ** 2) -@pytest.mark.parametrize("method_name", ("expval", "measurement")) -class TestExpval: - """Tests for the expval function""" - - wires = 2 - - @pytest.mark.parametrize( - "obs, expected", - [ - [qml.PauliX(0), -0.041892271271228736], - [qml.PauliX(1), 0.0], - [qml.PauliY(0), -0.5516350865364075], - [qml.PauliY(1), 0.0], - [qml.PauliZ(0), 0.8330328980789793], - [qml.PauliZ(1), 1.0], - ], - ) - def test_expval_qml_tape_wire0(self, obs, expected, tol, lightning_sv, method_name): - """Test expval with a circuit on wires=[0]""" - - x, y, z = [0.5, 0.3, -0.7] - statevector = lightning_sv(self.wires) - statevector.apply_operations( - [qml.RX(0.4, wires=[0]), qml.Rot(x, y, z, wires=[0]), qml.RY(-0.2, wires=[0])] - ) - - m = LightningMeasurements(statevector) - result = getattr(m, method_name)(qml.expval(obs)) - - assert np.allclose(result, expected, atol=tol, rtol=0) - - @pytest.mark.parametrize( - "obs, expected", - [ - [qml.PauliX(0), 0.0], - [qml.PauliX(1), -0.19866933079506122], - [qml.PauliY(0), -0.3894183423086505], - [qml.PauliY(1), 0.0], - [qml.PauliZ(0), 0.9210609940028852], - [qml.PauliZ(1), 0.9800665778412417], - ], - ) - def test_expval_wire01(self, obs, expected, tol, lightning_sv, method_name): - """Test expval with a circuit on wires=[0,1]""" - - statevector = lightning_sv(self.wires) - statevector.apply_operations([qml.RX(0.4, wires=[0]), qml.RY(-0.2, wires=[1])]) - - m = LightningMeasurements(statevector) - result = getattr(m, method_name)(qml.expval(obs)) - - assert np.allclose(result, expected, atol=tol, rtol=0) - - @pytest.mark.parametrize( - "obs, coeffs, expected", - [ - ([qml.PauliX(0) @ qml.PauliZ(1)], [1.0], 0.0), - ([qml.PauliZ(0) @ qml.PauliZ(1)], [1.0], math.cos(0.4) * math.cos(-0.2)), - ( - [ - qml.PauliX(0) @ qml.PauliZ(1), - qml.Hermitian( - [ - [1.0, 0.0, 0.0, 0.0], - [0.0, 3.0, 0.0, 0.0], - [0.0, 0.0, -1.0, 1.0], - [0.0, 0.0, 1.0, -2.0], - ], - wires=[0, 1], - ), - ], - [0.3, 1.0], - 0.9319728930156066, - ), - ], - ) - def test_expval_hamiltonian(self, obs, coeffs, expected, tol, lightning_sv, method_name): - """Test expval with Hamiltonian""" - ham = qml.Hamiltonian(coeffs, obs) - - statevector = lightning_sv(self.wires) - statevector.apply_operations([qml.RX(0.4, wires=[0]), qml.RY(-0.2, wires=[1])]) - - m = LightningMeasurements(statevector) - result = getattr(m, method_name)(qml.expval(ham)) - - assert np.allclose(result, expected, atol=tol, rtol=0) - - THETA = np.linspace(0.11, 1, 3) PHI = np.linspace(0.32, 1, 3) @pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI))) class TestExpval: - """Test expectation value calculations""" + """Test expectation value calculations.""" - def test_Identity(self, theta, phi, tol, lightning_sv): + def test_identity(self, theta, phi, tol, lightning_sv): """Tests applying identities.""" wires = 3 @@ -391,6 +297,48 @@ def test_single_wire_observables_expectation( assert np.allclose(result, expected, tol) +@pytest.mark.parametrize("method_name", ("expval", "measurement")) +class TestExpvalHamiltonian: + """Tests expval for Hamiltonians""" + + wires = 2 + + @pytest.mark.parametrize( + "obs, coeffs, expected", + [ + ([qml.PauliX(0) @ qml.PauliZ(1)], [1.0], 0.0), + ([qml.PauliZ(0) @ qml.PauliZ(1)], [1.0], math.cos(0.4) * math.cos(-0.2)), + ( + [ + qml.PauliX(0) @ qml.PauliZ(1), + qml.Hermitian( + [ + [1.0, 0.0, 0.0, 0.0], + [0.0, 3.0, 0.0, 0.0], + [0.0, 0.0, -1.0, 1.0], + [0.0, 0.0, 1.0, -2.0], + ], + wires=[0, 1], + ), + ], + [0.3, 1.0], + 0.9319728930156066, + ), + ], + ) + def test_expval_hamiltonian(self, obs, coeffs, expected, tol, lightning_sv, method_name): + """Test expval with Hamiltonian""" + ham = qml.Hamiltonian(coeffs, obs) + + statevector = lightning_sv(self.wires) + statevector.apply_operations([qml.RX(0.4, wires=[0]), qml.RY(-0.2, wires=[1])]) + + m = LightningMeasurements(statevector) + result = getattr(m, method_name)(qml.expval(ham)) + + assert np.allclose(result, expected, atol=tol, rtol=0) + + class TestSparseExpval: """Tests for the expval function""" From 36d810e5e9fd12d3ec1f3d1f0c540d4cdec81ad0 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Thu, 29 Feb 2024 10:49:32 -0500 Subject: [PATCH 089/428] isort+black --- .../lightning_qubit/_measurements.py | 12 +++++------- .../lightning_qubit/_state_vector.py | 14 +++++--------- tests/lightning_qubit/test_state_vector_class.py | 10 +++++----- 3 files changed, 15 insertions(+), 21 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/_measurements.py b/pennylane_lightning/lightning_qubit/_measurements.py index 520bd402a8..14ea6d26ca 100644 --- a/pennylane_lightning/lightning_qubit/_measurements.py +++ b/pennylane_lightning/lightning_qubit/_measurements.py @@ -17,22 +17,20 @@ # pylint: disable=import-error, no-name-in-module, ungrouped-imports try: - from pennylane_lightning.lightning_qubit_ops import ( - MeasurementsC64, - MeasurementsC128, - ) + from pennylane_lightning.lightning_qubit_ops import MeasurementsC64, MeasurementsC128 except ImportError: pass from typing import Callable, List -import numpy as np -from pennylane.measurements import StateMeasurement, MeasurementProcess, ExpectationMP -from pennylane.typing import TensorLike, Result +import numpy as np +from pennylane.measurements import ExpectationMP, MeasurementProcess, StateMeasurement from pennylane.tape import QuantumScript +from pennylane.typing import Result, TensorLike from pennylane.wires import Wires from pennylane_lightning.core._serialize import QuantumScriptSerializer + from ._state_vector import LightningStateVector diff --git a/pennylane_lightning/lightning_qubit/_state_vector.py b/pennylane_lightning/lightning_qubit/_state_vector.py index 3e041f34e1..d913379065 100644 --- a/pennylane_lightning/lightning_qubit/_state_vector.py +++ b/pennylane_lightning/lightning_qubit/_state_vector.py @@ -17,26 +17,22 @@ try: from pennylane_lightning.lightning_qubit_ops import ( - allocate_aligned_array, - get_alignment, - best_alignment, StateVectorC64, StateVectorC128, + allocate_aligned_array, + best_alignment, + get_alignment, ) except ImportError: pass from itertools import product -import numpy as np +import numpy as np import pennylane as qml +from pennylane import BasisState, DeviceError, StatePrep from pennylane.tape import QuantumScript from pennylane.wires import Wires -from pennylane import ( - BasisState, - StatePrep, - DeviceError, -) class LightningStateVector: diff --git a/tests/lightning_qubit/test_state_vector_class.py b/tests/lightning_qubit/test_state_vector_class.py index b1c4e3550d..f30f49dbe6 100644 --- a/tests/lightning_qubit/test_state_vector_class.py +++ b/tests/lightning_qubit/test_state_vector_class.py @@ -15,17 +15,17 @@ Unit tests for the serialization helper functions. """ -import pytest -from conftest import LightningDevice # tested device - import math + import numpy as np import pennylane as qml -from pennylane.wires import Wires +import pytest +from conftest import LightningDevice # tested device from pennylane.tape import QuantumScript +from pennylane.wires import Wires -from pennylane_lightning.lightning_qubit._state_vector import LightningStateVector from pennylane_lightning.lightning_qubit import LightningQubit +from pennylane_lightning.lightning_qubit._state_vector import LightningStateVector if not LightningQubit._CPP_BINARY_AVAILABLE: pytest.skip("No binary module found. Skipping.", allow_module_level=True) From 816955f8c1776d130c82bf910f40125424aec9d0 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Thu, 29 Feb 2024 11:03:33 -0500 Subject: [PATCH 090/428] review suggestion --- pennylane_lightning/lightning_qubit/_measurements.py | 3 +-- tests/lightning_qubit/test_state_vector_class.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/_measurements.py b/pennylane_lightning/lightning_qubit/_measurements.py index 14ea6d26ca..bc79618363 100644 --- a/pennylane_lightning/lightning_qubit/_measurements.py +++ b/pennylane_lightning/lightning_qubit/_measurements.py @@ -87,8 +87,7 @@ def state_diagonalizing_gates(self, measurementprocess: StateMeasurement) -> Ten self._qubit_state.apply_operations(measurementprocess.diagonalizing_gates()) state_array = self._qubit_state.state - total_wires = int(np.log2(state_array.size)) - wires = Wires(range(total_wires)) + wires = Wires(range(self._qubit_state.num_wires)) return measurementprocess.process_state(state_array, wires) # pylint: disable=protected-access diff --git a/tests/lightning_qubit/test_state_vector_class.py b/tests/lightning_qubit/test_state_vector_class.py index f30f49dbe6..11a295e7c3 100644 --- a/tests/lightning_qubit/test_state_vector_class.py +++ b/tests/lightning_qubit/test_state_vector_class.py @@ -34,7 +34,7 @@ pytest.skip("Exclusive tests for lightning.qubit. Skipping.", allow_module_level=True) -@pytest.mark.parametrize("num_wires", [3, 10]) +@pytest.mark.parametrize("num_wires", range(4)) @pytest.mark.parametrize("dtype", [np.complex64, np.complex128]) @pytest.mark.parametrize("device_name", ["lightning.qubit"]) def test_device_name_and_init(num_wires, dtype, device_name): From 940644c1ad948034b8ea99abd5240345e43d07f6 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Thu, 29 Feb 2024 15:15:57 -0500 Subject: [PATCH 091/428] fix docs --- pennylane_lightning/lightning_qubit/_measurements.py | 7 ++----- pennylane_lightning/lightning_qubit/_state_vector.py | 5 +---- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/_measurements.py b/pennylane_lightning/lightning_qubit/_measurements.py index bc79618363..8b14f8b674 100644 --- a/pennylane_lightning/lightning_qubit/_measurements.py +++ b/pennylane_lightning/lightning_qubit/_measurements.py @@ -65,12 +65,9 @@ def dtype(self): return self._dtype def _measurement_dtype(self): - """Binding to Lightning Managed state vector. + """Binding to Lightning Measurements C++ class. - Args: - dtype (complex): Data complex type - - Returns: the state vector class + Returns: the Measurements class """ return MeasurementsC64 if self.dtype == np.complex64 else MeasurementsC128 diff --git a/pennylane_lightning/lightning_qubit/_state_vector.py b/pennylane_lightning/lightning_qubit/_state_vector.py index d913379065..f33054c7da 100644 --- a/pennylane_lightning/lightning_qubit/_state_vector.py +++ b/pennylane_lightning/lightning_qubit/_state_vector.py @@ -82,10 +82,7 @@ def num_wires(self): return self._num_wires def _state_dtype(self): - """Binding to Lightning Managed state vector. - - Args: - dtype (complex): Data complex type + """Binding to Lightning Managed state vector C++ class. Returns: the state vector class """ From 3922c326115a50d0765d15c233c09c79419fa476 Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Mon, 4 Mar 2024 19:48:31 +0000 Subject: [PATCH 092/428] Add qml.var support. --- .../lightning_qubit/_measurements.py | 46 ++++++++++- .../test_measurements_class.py | 80 ++++++++++++++++++- 2 files changed, 124 insertions(+), 2 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/_measurements.py b/pennylane_lightning/lightning_qubit/_measurements.py index 8b14f8b674..dc3c5cdab3 100644 --- a/pennylane_lightning/lightning_qubit/_measurements.py +++ b/pennylane_lightning/lightning_qubit/_measurements.py @@ -24,7 +24,7 @@ from typing import Callable, List import numpy as np -from pennylane.measurements import ExpectationMP, MeasurementProcess, StateMeasurement +from pennylane.measurements import ExpectationMP, MeasurementProcess, StateMeasurement, VarianceMP from pennylane.tape import QuantumScript from pennylane.typing import Result, TensorLike from pennylane.wires import Wires @@ -123,6 +123,42 @@ def expval(self, measurementprocess: MeasurementProcess): measurementprocess.obs.name, measurementprocess.obs.wires ) + # pylint: disable=protected-access + def var(self, measurementprocess: MeasurementProcess): + """Variance of the supplied observable contained in the MeasurementProcess. + + Args: + measurementprocess (StateMeasurement): measurement to apply to the state + + Returns: + Variance of the observable + """ + + if measurementprocess.obs.name == "SparseHamiltonian": + # ensuring CSR sparse representation. + CSR_SparseHamiltonian = measurementprocess.obs.sparse_matrix( + wire_order=list(range(self._qubit_state.num_wires)) + ).tocsr(copy=False) + return self._measurement_lightning.var( + CSR_SparseHamiltonian.indptr, + CSR_SparseHamiltonian.indices, + CSR_SparseHamiltonian.data, + ) + + if ( + measurementprocess.obs.name in ["Hamiltonian", "Hermitian"] + or (measurementprocess.obs.arithmetic_depth > 0) + or isinstance(measurementprocess.obs.name, List) + ): + ob_serialized = QuantumScriptSerializer( + self._qubit_state.device_name, self.dtype == np.complex64 + )._ob(measurementprocess.obs) + return self._measurement_lightning.var(ob_serialized) + + return self._measurement_lightning.var( + measurementprocess.obs.name, measurementprocess.obs.wires + ) + def get_measurement_function( self, measurementprocess: MeasurementProcess ) -> Callable[[MeasurementProcess, TensorLike], TensorLike]: @@ -143,6 +179,14 @@ def get_measurement_function( return self.state_diagonalizing_gates return self.expval + if isinstance(measurementprocess, VarianceMP): + if measurementprocess.obs.name in [ + "Identity", + "Projector", + ]: + return self.state_diagonalizing_gates + return self.var + if measurementprocess.obs is None or measurementprocess.obs.has_diagonalizing_gates: return self.state_diagonalizing_gates diff --git a/tests/lightning_qubit/test_measurements_class.py b/tests/lightning_qubit/test_measurements_class.py index a9124901eb..b8dd6a68a1 100644 --- a/tests/lightning_qubit/test_measurements_class.py +++ b/tests/lightning_qubit/test_measurements_class.py @@ -20,6 +20,8 @@ import pytest from conftest import LightningDevice # tested device from pennylane.devices import DefaultQubit +from scipy.sparse import csr_matrix, random_array +from pennylane.measurements import VarianceMP try: from pennylane_lightning.lightning_qubit_ops import MeasurementsC64, MeasurementsC128 @@ -49,6 +51,17 @@ def _statevector(num_wires): return _statevector +def get_hermitian_matrix(n): + H = np.random.rand(n, n) + 1.0j * np.random.rand(n, n) + return H + np.conj(H).T + + +def get_sparse_hermitian_matrix(n): + H = random_array((n, n), density=0.15) + H = H + 1.0j * random_array((n, n), density=0.15) + return csr_matrix(H + H.conj().T) + + class CustomStateMeasurement(qml.measurements.StateMeasurement): def process_state(self, state, wire_order): return 1 @@ -81,7 +94,6 @@ def test_only_support_state_measurements(self, lightning_sv): "mp", ( qml.probs(wires=0), - qml.var(qml.Z(0)), qml.vn_entropy(wires=0), CustomStateMeasurement(), qml.expval(qml.Identity(0)), @@ -376,6 +388,72 @@ def test_sparse_Pauli_words(self, ham_terms, expected, tol, lightning_sv): assert np.allclose(result, expected, tol) +class TestMeasurements: + """Tests all measurements""" + + @staticmethod + def calculate_reference(tape, lightning_sv): + use_default = True + new_meas = [] + for m in tape.measurements: + # not supported by DefaultQubit + if isinstance(m, VarianceMP) and isinstance( + m.obs, (qml.Hamiltonian, qml.SparseHamiltonian) + ): + use_default = False + new_meas.append(m.__class__(qml.Hermitian(qml.matrix(m.obs), wires=m.obs.wires))) + continue + new_meas.append(m) + if use_default: + dev = DefaultQubit(max_workers=1) + program, _ = dev.preprocess() + tapes, transf_fn = program([tape]) + results = dev.execute(tapes) + return transf_fn(results) + + tape = qml.tape.QuantumScript(tape.operations, new_meas) + statevector = lightning_sv(tape.num_wires) + statevector = statevector.get_final_state(tape) + m = LightningMeasurements(statevector) + return m.measure_final_state(tape) + + @pytest.mark.parametrize("measurement", [qml.expval, qml.var]) + @pytest.mark.parametrize( + "observable", + ( + qml.PauliX(0), + qml.PauliY(1), + qml.PauliZ(2), + qml.sum(qml.PauliX(0), qml.PauliY(0)), + qml.prod(qml.PauliX(0), qml.PauliY(1)), + qml.s_prod(2.0, qml.PauliX(0)), + qml.Hermitian(get_hermitian_matrix(2**2), wires=[0, 1]), + qml.Hermitian(get_hermitian_matrix(2**2), wires=[2, 3]), + qml.Hamiltonian( + [1.0, 2.0, 3.0], [qml.PauliX(0), qml.PauliY(1), qml.PauliZ(2) @ qml.PauliZ(3)] + ), + qml.SparseHamiltonian(get_sparse_hermitian_matrix(2**4), wires=range(4)), + ), + ) + def test_single_return(self, measurement, observable, lightning_sv, tol): + n_qubits = 4 + n_layers = 1 + np.random.seed(0) + weights = np.random.rand(n_layers, n_qubits, 3) + ops = [qml.Hadamard(i) for i in range(n_qubits)] + ops += [qml.StronglyEntanglingLayers(weights, wires=range(n_qubits))] + measurements = [measurement(observable)] + tape = qml.tape.QuantumScript(ops, measurements) + + statevector = lightning_sv(n_qubits) + statevector = statevector.get_final_state(tape) + m = LightningMeasurements(statevector) + result = m.measure_final_state(tape) + expected = self.calculate_reference(tape, lightning_sv) + + assert np.allclose(result, expected, tol) + + class TestControlledOps: """Tests for controlled operations""" From 5b31b076bc6708cc41db887cf72a3b5d451a8eca Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Tue, 5 Mar 2024 01:25:02 +0000 Subject: [PATCH 093/428] Add probs support. --- .../lightning_qubit/_measurements.py | 38 ++++++- .../test_measurements_class.py | 102 +++++++++++++++--- 2 files changed, 124 insertions(+), 16 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/_measurements.py b/pennylane_lightning/lightning_qubit/_measurements.py index dc3c5cdab3..43d700aa0c 100644 --- a/pennylane_lightning/lightning_qubit/_measurements.py +++ b/pennylane_lightning/lightning_qubit/_measurements.py @@ -24,7 +24,13 @@ from typing import Callable, List import numpy as np -from pennylane.measurements import ExpectationMP, MeasurementProcess, StateMeasurement, VarianceMP +from pennylane.measurements import ( + ExpectationMP, + ProbabilityMP, + MeasurementProcess, + StateMeasurement, + VarianceMP, +) from pennylane.tape import QuantumScript from pennylane.typing import Result, TensorLike from pennylane.wires import Wires @@ -82,7 +88,6 @@ def state_diagonalizing_gates(self, measurementprocess: StateMeasurement) -> Ten TensorLike: the result of the measurement """ self._qubit_state.apply_operations(measurementprocess.diagonalizing_gates()) - state_array = self._qubit_state.state wires = Wires(range(self._qubit_state.num_wires)) return measurementprocess.process_state(state_array, wires) @@ -123,6 +128,32 @@ def expval(self, measurementprocess: MeasurementProcess): measurementprocess.obs.name, measurementprocess.obs.wires ) + # pylint: disable=protected-access + def probs(self, measurementprocess: MeasurementProcess): + """Probabilities of the supplied observable or wires contained in the MeasurementProcess. + + Args: + measurementprocess (StateMeasurement): measurement to apply to the state + + Returns: + Probabilities of the supplied observable or wires + """ + diagonalizing_gates = measurementprocess.diagonalizing_gates() + if diagonalizing_gates: + qubit_state = LightningStateVector( + num_wires=self._qubit_state.num_wires, + dtype=self._qubit_state.dtype, + device_name=self._qubit_state.device_name, + ) + qubit_state._apply_state_vector(self._state, range(self._qubit_state.num_wires)) + self._qubit_state.apply_operations(measurementprocess.diagonalizing_gates()) + results = self._measurement_lightning.probs(measurementprocess.wires.tolist()) + if diagonalizing_gates: + self._qubit_state._apply_state_vector( + qubit_state.state_vector, range(self._qubit_state.num_wires) + ) + return results + # pylint: disable=protected-access def var(self, measurementprocess: MeasurementProcess): """Variance of the supplied observable contained in the MeasurementProcess. @@ -179,6 +210,9 @@ def get_measurement_function( return self.state_diagonalizing_gates return self.expval + if isinstance(measurementprocess, ProbabilityMP): + return self.probs + if isinstance(measurementprocess, VarianceMP): if measurementprocess.obs.name in [ "Identity", diff --git a/tests/lightning_qubit/test_measurements_class.py b/tests/lightning_qubit/test_measurements_class.py index b8dd6a68a1..bdb5d218cc 100644 --- a/tests/lightning_qubit/test_measurements_class.py +++ b/tests/lightning_qubit/test_measurements_class.py @@ -15,13 +15,14 @@ import itertools import math +from typing import Sequence import numpy as np import pennylane as qml import pytest from conftest import LightningDevice # tested device from pennylane.devices import DefaultQubit from scipy.sparse import csr_matrix, random_array -from pennylane.measurements import VarianceMP +from pennylane.measurements import ProbabilityMP, VarianceMP try: from pennylane_lightning.lightning_qubit_ops import MeasurementsC64, MeasurementsC128 @@ -110,15 +111,15 @@ def test_state_diagonalizing_gates_measurements(self, lightning_sv, mp): @pytest.mark.parametrize( "obs", ( - qml.X(0), - qml.Y(0), - qml.Z(0), - qml.sum(qml.X(0), qml.Y(0)), - qml.prod(qml.X(0), qml.Y(1)), - qml.s_prod(2.0, qml.X(0)), - qml.Hamiltonian([1.0, 2.0], [qml.X(0), qml.Y(0)]), + qml.PauliX(0), + qml.PauliY(0), + qml.PauliZ(0), + qml.sum(qml.PauliX(0), qml.PauliY(0)), + qml.prod(qml.PauliX(0), qml.PauliY(1)), + qml.s_prod(2.0, qml.PauliX(0)), + qml.Hamiltonian([1.0, 2.0], [qml.PauliX(0), qml.PauliY(0)]), qml.Hermitian(np.eye(2), wires=0), - qml.SparseHamiltonian(qml.X.compute_sparse_matrix(), wires=0), + qml.SparseHamiltonian(qml.PauliX.compute_sparse_matrix(), wires=0), ), ) def test_expval_selected(self, lightning_sv, obs): @@ -168,7 +169,7 @@ def test_custom_measurement(self, lightning_sv, method_name): def test_measurement_with_diagonalizing_gates(self, lightning_sv, method_name): statevector = lightning_sv(num_wires=5) m = LightningMeasurements(statevector) - measurement = qml.probs(op=qml.X(0)) + measurement = qml.probs(op=qml.PauliX(0)) result = getattr(m, method_name)(measurement) assert qml.math.allclose(result, [0.5, 0.5]) @@ -417,7 +418,7 @@ def calculate_reference(tape, lightning_sv): m = LightningMeasurements(statevector) return m.measure_final_state(tape) - @pytest.mark.parametrize("measurement", [qml.expval, qml.var]) + @pytest.mark.parametrize("measurement", [qml.expval, qml.probs, qml.var]) @pytest.mark.parametrize( "observable", ( @@ -435,24 +436,97 @@ def calculate_reference(tape, lightning_sv): qml.SparseHamiltonian(get_sparse_hermitian_matrix(2**4), wires=range(4)), ), ) - def test_single_return(self, measurement, observable, lightning_sv, tol): + def test_single_return_value(self, measurement, observable, lightning_sv, tol): + if measurement is qml.probs and isinstance( + observable, + (qml.ops.Sum, qml.ops.SProd, qml.ops.Prod, qml.Hamiltonian, qml.SparseHamiltonian), + ): + return n_qubits = 4 n_layers = 1 np.random.seed(0) weights = np.random.rand(n_layers, n_qubits, 3) ops = [qml.Hadamard(i) for i in range(n_qubits)] ops += [qml.StronglyEntanglingLayers(weights, wires=range(n_qubits))] - measurements = [measurement(observable)] + measurements = [measurement(op=observable)] tape = qml.tape.QuantumScript(ops, measurements) + expected = self.calculate_reference(tape, lightning_sv) statevector = lightning_sv(n_qubits) statevector = statevector.get_final_state(tape) m = LightningMeasurements(statevector) result = m.measure_final_state(tape) - expected = self.calculate_reference(tape, lightning_sv) assert np.allclose(result, expected, tol) + @pytest.mark.parametrize("measurement", [qml.expval, qml.probs, qml.var]) + @pytest.mark.parametrize( + "obs0_", + ( + qml.PauliX(0), + qml.PauliY(1), + qml.PauliZ(2), + qml.sum(qml.PauliX(0), qml.PauliY(0)), + qml.prod(qml.PauliX(0), qml.PauliY(1)), + qml.s_prod(2.0, qml.PauliX(0)), + qml.Hermitian(get_hermitian_matrix(2), wires=[0]), + qml.Hermitian(get_hermitian_matrix(2**2), wires=[2, 3]), + qml.Hamiltonian( + [1.0, 2.0, 3.0], [qml.PauliX(0), qml.PauliY(1), qml.PauliZ(2) @ qml.PauliZ(3)] + ), + qml.SparseHamiltonian(get_sparse_hermitian_matrix(2**4), wires=range(4)), + ), + ) + @pytest.mark.parametrize( + "obs1_", + ( + qml.PauliX(0), + qml.PauliY(1), + qml.PauliZ(2), + qml.sum(qml.PauliX(0), qml.PauliY(0)), + qml.prod(qml.PauliX(0), qml.PauliY(1)), + qml.s_prod(2.0, qml.PauliX(0)), + qml.Hermitian(get_hermitian_matrix(2), wires=[0]), + qml.Hermitian(get_hermitian_matrix(2**2), wires=[2, 3]), + # qml.Hamiltonian( + # [1.0, 2.0, 3.0], [qml.PauliX(0), qml.PauliY(1), qml.PauliZ(2) @ qml.PauliZ(3)] + # ), + # qml.SparseHamiltonian(get_sparse_hermitian_matrix(2**4), wires=range(4)), + ), + ) + def test_double_return_value(self, measurement, obs0_, obs1_, lightning_sv, tol): + if measurement is qml.probs and isinstance( + obs0_, + (qml.ops.Sum, qml.ops.SProd, qml.ops.Prod, qml.Hamiltonian, qml.SparseHamiltonian), + ): + return + if measurement is qml.probs and isinstance( + obs1_, + (qml.ops.Sum, qml.ops.SProd, qml.ops.Prod, qml.Hamiltonian, qml.SparseHamiltonian), + ): + return + n_qubits = 4 + n_layers = 1 + np.random.seed(0) + weights = np.random.rand(n_layers, n_qubits, 3) + ops = [qml.Hadamard(i) for i in range(n_qubits)] + ops += [qml.StronglyEntanglingLayers(weights, wires=range(n_qubits))] + measurements = [measurement(op=obs0_), measurement(op=obs1_)] + tape = qml.tape.QuantumScript(ops, measurements) + + expected = self.calculate_reference(tape, lightning_sv) + if len(expected) == 1: + expected = expected[0] + statevector = lightning_sv(n_qubits) + statevector = statevector.get_final_state(tape) + m = LightningMeasurements(statevector) + result = m.measure_final_state(tape) + + assert isinstance(result, Sequence) + assert len(result) == len(expected) + for r, e in zip(result, expected): + assert np.allclose(r, e, rtol=1.0e-5, atol=0.0) + class TestControlledOps: """Tests for controlled operations""" From b54d3eaee5ab6902b3ee35f570d3db38cb91829b Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Tue, 5 Mar 2024 08:33:08 -0500 Subject: [PATCH 094/428] increase tolerance --- tests/lightning_qubit/test_measurements_class.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/lightning_qubit/test_measurements_class.py b/tests/lightning_qubit/test_measurements_class.py index a9124901eb..8ad4cb1caa 100644 --- a/tests/lightning_qubit/test_measurements_class.py +++ b/tests/lightning_qubit/test_measurements_class.py @@ -468,7 +468,7 @@ def test_controlled_qubit_gates(self, operation, n_qubits, control_value, tol, l result = m.measure_final_state(tape) expected = self.calculate_reference(tape) - assert np.allclose(result, expected, tol) + assert np.allclose(result, expected, tol * 10) def test_controlled_qubit_unitary_from_op(self, tol, lightning_sv): n_qubits = 10 From 3703b94692f46c1aa30049eb8b149310944972aa Mon Sep 17 00:00:00 2001 From: Dev version update bot Date: Tue, 5 Mar 2024 14:59:58 +0000 Subject: [PATCH 095/428] Auto update version --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index 512bfe6499..5ef6d062a5 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.36.0-dev1" +__version__ = "0.36.0-dev2" From 8519c1c798082bffbd8b2e0da88c717df7d70e16 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Tue, 5 Mar 2024 10:05:45 -0500 Subject: [PATCH 096/428] isort --- pennylane_lightning/lightning_qubit/_measurements.py | 5 ++++- tests/lightning_qubit/test_measurements_class.py | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/_measurements.py b/pennylane_lightning/lightning_qubit/_measurements.py index 8b14f8b674..79c40d681e 100644 --- a/pennylane_lightning/lightning_qubit/_measurements.py +++ b/pennylane_lightning/lightning_qubit/_measurements.py @@ -17,7 +17,10 @@ # pylint: disable=import-error, no-name-in-module, ungrouped-imports try: - from pennylane_lightning.lightning_qubit_ops import MeasurementsC64, MeasurementsC128 + from pennylane_lightning.lightning_qubit_ops import ( + MeasurementsC64, + MeasurementsC128, + ) except ImportError: pass diff --git a/tests/lightning_qubit/test_measurements_class.py b/tests/lightning_qubit/test_measurements_class.py index 8ad4cb1caa..ff951385f9 100644 --- a/tests/lightning_qubit/test_measurements_class.py +++ b/tests/lightning_qubit/test_measurements_class.py @@ -22,7 +22,10 @@ from pennylane.devices import DefaultQubit try: - from pennylane_lightning.lightning_qubit_ops import MeasurementsC64, MeasurementsC128 + from pennylane_lightning.lightning_qubit_ops import ( + MeasurementsC64, + MeasurementsC128, + ) except ImportError: pass From 60aef32a58ede31cf03c390de42c8f4b0a27a98c Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Tue, 5 Mar 2024 15:24:11 +0000 Subject: [PATCH 097/428] Add double-obs tests. --- .../lightning_qubit/_measurements.py | 13 ++++--------- tests/lightning_qubit/test_measurements_class.py | 12 ++++++------ 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/_measurements.py b/pennylane_lightning/lightning_qubit/_measurements.py index 43d700aa0c..e5102a7e40 100644 --- a/pennylane_lightning/lightning_qubit/_measurements.py +++ b/pennylane_lightning/lightning_qubit/_measurements.py @@ -24,6 +24,7 @@ from typing import Callable, List import numpy as np +import pennylane as qml from pennylane.measurements import ( ExpectationMP, ProbabilityMP, @@ -140,17 +141,11 @@ def probs(self, measurementprocess: MeasurementProcess): """ diagonalizing_gates = measurementprocess.diagonalizing_gates() if diagonalizing_gates: - qubit_state = LightningStateVector( - num_wires=self._qubit_state.num_wires, - dtype=self._qubit_state.dtype, - device_name=self._qubit_state.device_name, - ) - qubit_state._apply_state_vector(self._state, range(self._qubit_state.num_wires)) - self._qubit_state.apply_operations(measurementprocess.diagonalizing_gates()) + self._qubit_state.apply_operations(diagonalizing_gates) results = self._measurement_lightning.probs(measurementprocess.wires.tolist()) if diagonalizing_gates: - self._qubit_state._apply_state_vector( - qubit_state.state_vector, range(self._qubit_state.num_wires) + self._qubit_state.apply_operations( + [qml.adjoint(g) for g in reversed(diagonalizing_gates)] ) return results diff --git a/tests/lightning_qubit/test_measurements_class.py b/tests/lightning_qubit/test_measurements_class.py index bdb5d218cc..e9ac005350 100644 --- a/tests/lightning_qubit/test_measurements_class.py +++ b/tests/lightning_qubit/test_measurements_class.py @@ -94,7 +94,6 @@ def test_only_support_state_measurements(self, lightning_sv): @pytest.mark.parametrize( "mp", ( - qml.probs(wires=0), qml.vn_entropy(wires=0), CustomStateMeasurement(), qml.expval(qml.Identity(0)), @@ -488,10 +487,10 @@ def test_single_return_value(self, measurement, observable, lightning_sv, tol): qml.s_prod(2.0, qml.PauliX(0)), qml.Hermitian(get_hermitian_matrix(2), wires=[0]), qml.Hermitian(get_hermitian_matrix(2**2), wires=[2, 3]), - # qml.Hamiltonian( - # [1.0, 2.0, 3.0], [qml.PauliX(0), qml.PauliY(1), qml.PauliZ(2) @ qml.PauliZ(3)] - # ), - # qml.SparseHamiltonian(get_sparse_hermitian_matrix(2**4), wires=range(4)), + qml.Hamiltonian( + [1.0, 2.0, 3.0], [qml.PauliX(0), qml.PauliY(1), qml.PauliZ(2) @ qml.PauliZ(3)] + ), + qml.SparseHamiltonian(get_sparse_hermitian_matrix(2**4), wires=range(4)), ), ) def test_double_return_value(self, measurement, obs0_, obs1_, lightning_sv, tol): @@ -524,8 +523,9 @@ def test_double_return_value(self, measurement, obs0_, obs1_, lightning_sv, tol) assert isinstance(result, Sequence) assert len(result) == len(expected) + # a few tests fail in single precision, and hence we increase the tolerance for r, e in zip(result, expected): - assert np.allclose(r, e, rtol=1.0e-5, atol=0.0) + assert np.allclose(r, e, max(tol, 1.0e-5)) class TestControlledOps: From 6308ba2057a01241505b29d4b85e2f83fead5e33 Mon Sep 17 00:00:00 2001 From: Amintor Dusko <87949283+AmintorDusko@users.noreply.github.com> Date: Mon, 4 Mar 2024 10:39:20 -0500 Subject: [PATCH 098/428] Pin pytest version (#624) * update dev version * update changelog * pin pytest version in requirement files * add a requirements file for tests against Pennylane master * update wheels' workflows --- .github/CHANGELOG.md | 3 +++ .github/workflows/wheel_linux_aarch64.yml | 4 +--- .github/workflows/wheel_linux_x86_64.yml | 5 +---- .github/workflows/wheel_macos_arm64.yml | 5 +---- .github/workflows/wheel_macos_x86_64.yml | 7 ++----- .github/workflows/wheel_win_x86_64.yml | 5 +---- requirements-dev.txt | 2 +- requirements-tests.txt | 6 ++++++ requirements.txt | 2 +- 9 files changed, 17 insertions(+), 22 deletions(-) create mode 100644 requirements-tests.txt diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index 289b5f73c2..4519b40f6d 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -84,6 +84,9 @@ * Fix apply state vector when using a Lightning handle. [(#622)](https://github.com/PennyLaneAI/pennylane-lightning/pull/622) +* Pinning Pytest to a version compatible with Flaky. + [(#624)](https://github.com/PennyLaneAI/pennylane-lightning/pull/624) + ### Contributors This release contains contributions from (in alphabetical order): diff --git a/.github/workflows/wheel_linux_aarch64.yml b/.github/workflows/wheel_linux_aarch64.yml index 8624ad129c..1a10b46a30 100644 --- a/.github/workflows/wheel_linux_aarch64.yml +++ b/.github/workflows/wheel_linux_aarch64.yml @@ -139,10 +139,8 @@ jobs: CIBW_BUILD_VERBOSITY: 3 - CIBW_TEST_REQUIRES: pytest pytest-cov pytest-mock flaky - CIBW_BEFORE_TEST: | - python -m pip install pytest-benchmark git+https://github.com/PennyLaneAI/pennylane.git@master + python -m pip install -r requirements-tests.txt if ${{ matrix.pl_backend == 'lightning_kokkos'}}; then SKIP_COMPILATION=True PL_BACKEND="lightning_qubit" pip install -e . -vv; fi CIBW_TEST_COMMAND: | diff --git a/.github/workflows/wheel_linux_x86_64.yml b/.github/workflows/wheel_linux_x86_64.yml index 9ce3c2d451..35e48a29f7 100644 --- a/.github/workflows/wheel_linux_x86_64.yml +++ b/.github/workflows/wheel_linux_x86_64.yml @@ -149,11 +149,8 @@ jobs: PATH="/opt/rh/devtoolset-11/root/usr/bin:$PATH" \ PL_BACKEND="${{ matrix.pl_backend }}" - # Testing of built wheels - CIBW_TEST_REQUIRES: pytest pytest-cov pytest-mock flaky - CIBW_BEFORE_TEST: | - python -m pip install pytest-benchmark git+https://github.com/PennyLaneAI/pennylane.git@master + python -m pip install -r requirements-tests.txt if ${{ matrix.pl_backend == 'lightning_kokkos'}}; then SKIP_COMPILATION=True PL_BACKEND="lightning_qubit" pip install -e . -vv; fi CIBW_TEST_COMMAND: | diff --git a/.github/workflows/wheel_macos_arm64.yml b/.github/workflows/wheel_macos_arm64.yml index b9dfa43d6b..a381e5305a 100644 --- a/.github/workflows/wheel_macos_arm64.yml +++ b/.github/workflows/wheel_macos_arm64.yml @@ -89,11 +89,8 @@ jobs: CMAKE_ARGS="-DCMAKE_CXX_COMPILER_TARGET=arm64-apple-macos11 -DCMAKE_SYSTEM_NAME=Darwin -DCMAKE_SYSTEM_PROCESSOR=ARM64 -DENABLE_OPENMP=OFF" \ PL_BACKEND="${{ matrix.pl_backend }}" - # Testing of built wheels - CIBW_TEST_REQUIRES: pytest pytest-cov pytest-mock flaky - CIBW_BEFORE_TEST: | - python -m pip install pytest-benchmark git+https://github.com/PennyLaneAI/pennylane.git@master + python -m pip install -r requirements-tests.txt if ${{ matrix.pl_backend == 'lightning_kokkos'}}; then SKIP_COMPILATION=True PL_BACKEND="lightning_qubit" pip install -e . -vv; fi CIBW_TEST_COMMAND: | diff --git a/.github/workflows/wheel_macos_x86_64.yml b/.github/workflows/wheel_macos_x86_64.yml index a7c68734c8..e727468fe4 100644 --- a/.github/workflows/wheel_macos_x86_64.yml +++ b/.github/workflows/wheel_macos_x86_64.yml @@ -21,7 +21,7 @@ concurrency: cancel-in-progress: true jobs: - set_wheel_build_matrix: + set_wheel_build_matrix: name: "Set wheel build matrix" uses: ./.github/workflows/set_wheel_build_matrix.yml with: @@ -137,11 +137,8 @@ jobs: PL_BACKEND: ${{ matrix.pl_backend }} - # Testing of built wheels - CIBW_TEST_REQUIRES: pytest pytest-cov pytest-mock flaky - CIBW_BEFORE_TEST: | - python -m pip install pytest-benchmark git+https://github.com/PennyLaneAI/pennylane.git@master + python -m pip install -r requirements-tests.txt if ${{ matrix.pl_backend == 'lightning_kokkos'}}; then SKIP_COMPILATION=True PL_BACKEND="lightning_qubit" pip install -e . -vv; fi CIBW_TEST_COMMAND: | diff --git a/.github/workflows/wheel_win_x86_64.yml b/.github/workflows/wheel_win_x86_64.yml index 4dc8ff4e48..7ba0704c0b 100644 --- a/.github/workflows/wheel_win_x86_64.yml +++ b/.github/workflows/wheel_win_x86_64.yml @@ -126,11 +126,8 @@ jobs: CIBW_BEFORE_BUILD: | python -m pip install pybind11 cmake~=3.24.0 build - # Testing of built wheels - CIBW_TEST_REQUIRES: pytest pytest-cov pytest-mock flaky - CIBW_BEFORE_TEST: | - python -m pip install pytest-benchmark git+https://github.com/PennyLaneAI/pennylane.git@master + python -m pip install -r requirements-tests.txt CIBW_TEST_COMMAND: | pl-device-test --device=lightning.qubit --skip-ops -x --tb=short --no-flaky-report diff --git a/requirements-dev.txt b/requirements-dev.txt index 94456d4e0f..7a0230999c 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -3,7 +3,7 @@ git+https://github.com/PennyLaneAI/pennylane.git@master ninja flaky pybind11 -pytest +pytest~=8.0.0 pytest-benchmark pytest-cov pytest-mock diff --git a/requirements-tests.txt b/requirements-tests.txt new file mode 100644 index 0000000000..41c3a9f539 --- /dev/null +++ b/requirements-tests.txt @@ -0,0 +1,6 @@ +pytest~=8.0.0 +pytest-cov +pytest-mock +flaky +pytest-benchmark +git+https://github.com/PennyLaneAI/pennylane.git@master \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 607a6c007f..c888135fa8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,6 +2,6 @@ ninja flaky pennylane>=0.34 pybind11 -pytest +pytest~=8.0.0 pytest-cov pytest-mock \ No newline at end of file From 3a08c6236e3c9a17d35280059a51f950516a357a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 4 Mar 2024 15:34:24 -0500 Subject: [PATCH 099/428] Version Bump (#626) * post release version bump * trigger CI --------- Co-authored-by: AmintorDusko Co-authored-by: AmintorDusko --- .github/CHANGELOG.md | 20 +++++++++++++++++++- pennylane_lightning/core/_version.py | 2 +- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index 4519b40f6d..8cf14f3bf0 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -1,4 +1,22 @@ -# Release 0.35.0-dev +# Release 0.36.0-dev + +### New features since last release + +### Breaking changes + +### Improvements + +### Documentation + +### Bug fixes + +### Contributors + +This release contains contributions from (in alphabetical order): + +--- + +# Release 0.35.0 ### New features since last release diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index 60da66632b..89f11c0a16 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.35.0-dev22" +__version__ = "0.36.0-dev" From daab5ad1bc367f8d1481725fd9818c14af4b6236 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Tue, 5 Mar 2024 08:33:08 -0500 Subject: [PATCH 100/428] increase tolerance --- tests/lightning_qubit/test_measurements_class.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/lightning_qubit/test_measurements_class.py b/tests/lightning_qubit/test_measurements_class.py index e9ac005350..a22c129e32 100644 --- a/tests/lightning_qubit/test_measurements_class.py +++ b/tests/lightning_qubit/test_measurements_class.py @@ -620,7 +620,7 @@ def test_controlled_qubit_gates(self, operation, n_qubits, control_value, tol, l result = m.measure_final_state(tape) expected = self.calculate_reference(tape) - assert np.allclose(result, expected, tol) + assert np.allclose(result, expected, tol * 10) def test_controlled_qubit_unitary_from_op(self, tol, lightning_sv): n_qubits = 10 From 078c8d8f3f80f34a1bcf78b43205ca8c7c2e0fc2 Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Tue, 5 Mar 2024 09:24:41 -0500 Subject: [PATCH 101/428] Introduce isort. (#623) * Introduce isort. * Auto update version * Update changelog * Auto update version * Update changelog. * trigger ci --------- Co-authored-by: Dev version update bot --- .github/CHANGELOG.md | 5 ++ Makefile | 1 + mpitests/conftest.py | 4 +- mpitests/test_adjoint_jacobian.py | 12 ++-- mpitests/test_apply.py | 8 +-- mpitests/test_device.py | 6 +- mpitests/test_expval.py | 5 +- mpitests/test_measurements_sparse.py | 8 +-- mpitests/test_probs.py | 13 ++--- pennylane_lightning/core/_serialize.py | 15 ++--- pennylane_lightning/core/_version.py | 2 +- pennylane_lightning/core/lightning_base.py | 14 ++--- pennylane_lightning/lightning_gpu/__init__.py | 1 + .../lightning_gpu/lightning_gpu.py | 56 ++++++++++--------- .../lightning_kokkos/__init__.py | 1 + .../lightning_kokkos/lightning_kokkos.py | 35 ++++++------ .../lightning_qubit/__init__.py | 1 + .../lightning_qubit/lightning_qubit.py | 32 +++++------ requirements-dev.txt | 1 + tests/conftest.py | 4 +- .../test_measurements_samples_MCMC.py | 6 +- tests/test_adjoint_jacobian.py | 14 ++--- tests/test_apply.py | 10 +++- tests/test_arrays.py | 3 +- tests/test_binary_info.py | 7 ++- tests/test_comparison.py | 9 +-- tests/test_decomposition.py | 5 +- tests/test_device.py | 6 +- tests/test_execute.py | 4 +- tests/test_expval.py | 4 +- tests/test_gates.py | 7 +-- tests/test_measurements.py | 13 ++--- tests/test_measurements_sparse.py | 6 +- tests/test_serialize.py | 47 ++++++++-------- tests/test_serialize_chunk_obs.py | 8 +-- tests/test_serialize_no_binaries.py | 2 +- tests/test_var.py | 5 +- tests/test_vjp.py | 7 ++- 38 files changed, 196 insertions(+), 191 deletions(-) diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index 8cf14f3bf0..a807815c9b 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -6,6 +6,9 @@ ### Improvements +* Add `isort` to `requirements-dev.txt` and run before `black` upon `make format` to sort Python imports. + [(#623)](https://github.com/PennyLaneAI/pennylane-lightning/pull/623) + ### Documentation ### Bug fixes @@ -14,6 +17,8 @@ This release contains contributions from (in alphabetical order): +Vincent Michaud-Rioux + --- # Release 0.35.0 diff --git a/Makefile b/Makefile index 8d42ba7a8f..eb9f9557e2 100644 --- a/Makefile +++ b/Makefile @@ -111,6 +111,7 @@ format-cpp: ./bin/format $(CHECK) ./pennylane_lightning format-python: + isort --profile black ./pennylane_lightning/ ./mpitests ./tests $(CHECK) black -l 100 ./pennylane_lightning/ ./mpitests ./tests $(CHECK) .PHONY: check-tidy diff --git a/mpitests/conftest.py b/mpitests/conftest.py index 09ab802b05..a2084f2a5d 100644 --- a/mpitests/conftest.py +++ b/mpitests/conftest.py @@ -18,10 +18,10 @@ import itertools import os -import pytest -from pennylane import numpy as np import pennylane as qml +import pytest +from pennylane import numpy as np # Tuple passed to distributed device ctor # np.complex for data type and True or False diff --git a/mpitests/test_adjoint_jacobian.py b/mpitests/test_adjoint_jacobian.py index 8d6cc8e219..5272f72062 100644 --- a/mpitests/test_adjoint_jacobian.py +++ b/mpitests/test_adjoint_jacobian.py @@ -17,14 +17,16 @@ # pylint: disable=protected-access,cell-var-from-loop,c-extension-no-member import itertools import math -from mpi4py import MPI -import pytest -from conftest import device_name, LightningDevice as ld -from scipy.stats import unitary_group import pennylane as qml +import pytest +from conftest import LightningDevice as ld +from conftest import device_name +from mpi4py import MPI +from pennylane import QNode from pennylane import numpy as np -from pennylane import QNode, qnode +from pennylane import qnode +from scipy.stats import unitary_group I, X, Y, Z = ( np.eye(2), diff --git a/mpitests/test_apply.py b/mpitests/test_apply.py index ad9e474fb4..87152ee6fb 100644 --- a/mpitests/test_apply.py +++ b/mpitests/test_apply.py @@ -16,14 +16,12 @@ """ # pylint: disable=protected-access,cell-var-from-loop,c-extension-no-member import itertools -from mpi4py import MPI -import pytest - -from conftest import TOL_STOCHASTIC, device_name, fixture_params import numpy as np import pennylane as qml - +import pytest +from conftest import TOL_STOCHASTIC, device_name, fixture_params +from mpi4py import MPI numQubits = 8 diff --git a/mpitests/test_device.py b/mpitests/test_device.py index d9761bf148..03a1880114 100644 --- a/mpitests/test_device.py +++ b/mpitests/test_device.py @@ -16,10 +16,10 @@ """ # pylint: disable=protected-access,unused-variable,missing-function-docstring,c-extension-no-member -import pytest -from conftest import device_name, LightningDevice as ld - import pennylane as qml +import pytest +from conftest import LightningDevice as ld +from conftest import device_name from mpi4py import MPI if not ld._CPP_BINARY_AVAILABLE: diff --git a/mpitests/test_expval.py b/mpitests/test_expval.py index 9db1e76b31..d020471c03 100644 --- a/mpitests/test_expval.py +++ b/mpitests/test_expval.py @@ -16,11 +16,10 @@ """ # pylint: disable=protected-access,too-few-public-methods,unused-import,missing-function-docstring,too-many-arguments,c-extension-no-member -import pytest -from conftest import THETA, PHI, VARPHI, device_name - import numpy as np import pennylane as qml +import pytest +from conftest import PHI, THETA, VARPHI, device_name from mpi4py import MPI diff --git a/mpitests/test_measurements_sparse.py b/mpitests/test_measurements_sparse.py index 4ea2856289..7ca88867eb 100644 --- a/mpitests/test_measurements_sparse.py +++ b/mpitests/test_measurements_sparse.py @@ -16,12 +16,12 @@ """ # pylint: disable=protected-access,too-few-public-methods,unused-import,missing-function-docstring,too-many-arguments -import pytest -from conftest import device_name, LightningDevice as ld -from mpi4py import MPI - import numpy as np import pennylane as qml +import pytest +from conftest import LightningDevice as ld +from conftest import device_name +from mpi4py import MPI from pennylane import qchem if not ld._CPP_BINARY_AVAILABLE: diff --git a/mpitests/test_probs.py b/mpitests/test_probs.py index f07a00ba6f..b2f57f733a 100644 --- a/mpitests/test_probs.py +++ b/mpitests/test_probs.py @@ -12,16 +12,13 @@ """ Unit tests for the :mod:`pennylane_lightning.LightningGPU` device (MPI). """ -# pylint: disable=missing-function-docstring,unnecessary-comprehension,too-many-arguments,wrong-import-order,unused-variable,c-extension-no-member -from mpi4py import MPI -import pytest - -from conftest import ( - device_name, -) - import numpy as np import pennylane as qml +import pytest +from conftest import device_name + +# pylint: disable=missing-function-docstring,unnecessary-comprehension,too-many-arguments,wrong-import-order,unused-variable,c-extension-no-member +from mpi4py import MPI numQubits = 8 diff --git a/pennylane_lightning/core/_serialize.py b/pennylane_lightning/core/_serialize.py index 50810608c7..2c85333b05 100644 --- a/pennylane_lightning/core/_serialize.py +++ b/pennylane_lightning/core/_serialize.py @@ -15,25 +15,26 @@ Helper functions for serializing quantum tapes. """ from typing import List, Tuple + import numpy as np from pennylane import ( BasisState, + DeviceError, Hadamard, + Hamiltonian, + Identity, PauliX, PauliY, PauliZ, - Identity, - StatePrep, + QubitUnitary, Rot, - Hamiltonian, SparseHamiltonian, - QubitUnitary, + StatePrep, + matrix, ) +from pennylane.math import unwrap from pennylane.operation import Tensor from pennylane.tape import QuantumTape -from pennylane.math import unwrap - -from pennylane import matrix, DeviceError pauli_name_map = { "I": "Identity", diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index 89f11c0a16..512bfe6499 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.36.0-dev" +__version__ = "0.36.0-dev1" diff --git a/pennylane_lightning/core/lightning_base.py b/pennylane_lightning/core/lightning_base.py index e17f83fe3b..36fa4c504f 100644 --- a/pennylane_lightning/core/lightning_base.py +++ b/pennylane_lightning/core/lightning_base.py @@ -16,25 +16,19 @@ This module contains the base class for all PennyLane Lightning simulator devices, and interfaces with C++ for improved performance. """ -from typing import List from itertools import islice, product -import numpy as np - +from typing import List +import numpy as np import pennylane as qml -from pennylane import ( - BasisState, - QubitDevice, - StatePrep, -) +from pennylane import BasisState, QubitDevice, StatePrep from pennylane.devices import DefaultQubitLegacy from pennylane.measurements import MeasurementProcess from pennylane.operation import Operation from pennylane.wires import Wires - -from ._version import __version__ from ._serialize import QuantumScriptSerializer +from ._version import __version__ def _chunk_iterable(iteration, num_chunks): diff --git a/pennylane_lightning/lightning_gpu/__init__.py b/pennylane_lightning/lightning_gpu/__init__.py index 54ce56655d..e88765aa28 100644 --- a/pennylane_lightning/lightning_gpu/__init__.py +++ b/pennylane_lightning/lightning_gpu/__init__.py @@ -14,4 +14,5 @@ """PennyLane lightning_gpu package.""" from pennylane_lightning.core import __version__ + from .lightning_gpu import LightningGPU diff --git a/pennylane_lightning/lightning_gpu/lightning_gpu.py b/pennylane_lightning/lightning_gpu/lightning_gpu.py index ca1c89b29b..be36ff548d 100644 --- a/pennylane_lightning/lightning_gpu/lightning_gpu.py +++ b/pennylane_lightning/lightning_gpu/lightning_gpu.py @@ -17,36 +17,34 @@ interfaces with the NVIDIA cuQuantum cuStateVec simulator library for GPU-enabled calculations. """ -from warnings import warn from pathlib import Path +from warnings import warn + import numpy as np -from pennylane_lightning.core.lightning_base import ( - LightningBase, - LightningBaseFallBack, -) +from pennylane_lightning.core.lightning_base import LightningBase, LightningBaseFallBack try: from pennylane_lightning.lightning_gpu_ops import ( - backend_info, - StateVectorC128, - StateVectorC64, - MeasurementsC128, + DevPool, MeasurementsC64, - is_gpu_supported, + MeasurementsC128, + StateVectorC64, + StateVectorC128, + backend_info, get_gpu_arch, - DevPool, + is_gpu_supported, ) try: # pylint: disable=no-name-in-module from pennylane_lightning.lightning_gpu_ops import ( - StateVectorMPIC128, - StateVectorMPIC64, - MeasurementsMPIC128, + DevTag, MeasurementsMPIC64, + MeasurementsMPIC128, MPIManager, - DevTag, + StateVectorMPIC64, + StateVectorMPIC128, ) MPI_SUPPORT = True @@ -75,40 +73,44 @@ LGPU_CPP_BINARY_AVAILABLE = False if LGPU_CPP_BINARY_AVAILABLE: - from typing import List, Union from itertools import product + from typing import List, Union + import pennylane as qml from pennylane import ( - math, BasisState, - StatePrep, DeviceError, Projector, - Rot, QuantumFunctionError, + Rot, + StatePrep, + math, ) + from pennylane.measurements import Expectation, MeasurementProcess, State from pennylane.operation import Tensor from pennylane.ops.op_math import Adjoint - from pennylane.measurements import Expectation, MeasurementProcess, State from pennylane.wires import Wires - import pennylane as qml - - # pylint: disable=import-error, no-name-in-module, ungrouped-imports - from pennylane_lightning.core._serialize import QuantumScriptSerializer, global_phase_diagonal - from pennylane_lightning.core._version import __version__ + # pylint: disable=no-name-in-module, ungrouped-imports from pennylane_lightning.lightning_gpu_ops.algorithms import ( AdjointJacobianC64, - create_ops_listC64, AdjointJacobianC128, + create_ops_listC64, create_ops_listC128, ) + # pylint: disable=import-error, no-name-in-module, ungrouped-imports + from pennylane_lightning.core._serialize import ( + QuantumScriptSerializer, + global_phase_diagonal, + ) + from pennylane_lightning.core._version import __version__ + if MPI_SUPPORT: from pennylane_lightning.lightning_gpu_ops.algorithmsMPI import ( AdjointJacobianMPIC64, - create_ops_listMPIC64, AdjointJacobianMPIC128, + create_ops_listMPIC64, create_ops_listMPIC128, ) diff --git a/pennylane_lightning/lightning_kokkos/__init__.py b/pennylane_lightning/lightning_kokkos/__init__.py index 73db3196de..7b1045d540 100644 --- a/pennylane_lightning/lightning_kokkos/__init__.py +++ b/pennylane_lightning/lightning_kokkos/__init__.py @@ -14,4 +14,5 @@ """PennyLane lightning_kokkos package.""" from pennylane_lightning.core import __version__ + from .lightning_kokkos import LightningKokkos diff --git a/pennylane_lightning/lightning_kokkos/lightning_kokkos.py b/pennylane_lightning/lightning_kokkos/lightning_kokkos.py index 9be3a68808..9844d86ec9 100644 --- a/pennylane_lightning/lightning_kokkos/lightning_kokkos.py +++ b/pennylane_lightning/lightning_kokkos/lightning_kokkos.py @@ -17,8 +17,9 @@ interfaces with C++ for fast linear algebra calculations. """ -from warnings import warn from pathlib import Path +from warnings import warn + import numpy as np from pennylane_lightning.core.lightning_base import ( @@ -30,14 +31,14 @@ try: # pylint: disable=import-error, no-name-in-module from pennylane_lightning.lightning_kokkos_ops import ( - allocate_aligned_array, - backend_info, InitializationSettings, - MeasurementsC128, MeasurementsC64, - print_configuration, - StateVectorC128, + MeasurementsC128, StateVectorC64, + StateVectorC128, + allocate_aligned_array, + backend_info, + print_configuration, ) LK_CPP_BINARY_AVAILABLE = True @@ -45,32 +46,34 @@ LK_CPP_BINARY_AVAILABLE = False if LK_CPP_BINARY_AVAILABLE: - from typing import List from os import getenv + from typing import List + import pennylane as qml from pennylane import ( - math, BasisState, - StatePrep, - Projector, - Rot, DeviceError, + Projector, QuantumFunctionError, + Rot, + StatePrep, + math, ) + from pennylane.measurements import Expectation, MeasurementProcess, State from pennylane.operation import Tensor from pennylane.ops.op_math import Adjoint - from pennylane.measurements import MeasurementProcess, Expectation, State from pennylane.wires import Wires - import pennylane as qml - # pylint: disable=import-error, no-name-in-module, ungrouped-imports - from pennylane_lightning.core._serialize import QuantumScriptSerializer, global_phase_diagonal + from pennylane_lightning.core._serialize import ( + QuantumScriptSerializer, + global_phase_diagonal, + ) from pennylane_lightning.core._version import __version__ from pennylane_lightning.lightning_kokkos_ops.algorithms import ( AdjointJacobianC64, - create_ops_listC64, AdjointJacobianC128, + create_ops_listC64, create_ops_listC128, ) diff --git a/pennylane_lightning/lightning_qubit/__init__.py b/pennylane_lightning/lightning_qubit/__init__.py index 53e50cbf00..a1b792afde 100644 --- a/pennylane_lightning/lightning_qubit/__init__.py +++ b/pennylane_lightning/lightning_qubit/__init__.py @@ -14,4 +14,5 @@ """PennyLane lightning_qubit package.""" from pennylane_lightning.core import __version__ + from .lightning_qubit import LightningQubit diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit.py b/pennylane_lightning/lightning_qubit/lightning_qubit.py index 51fa9d4c14..f220653311 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit.py @@ -17,8 +17,9 @@ interfaces with C++ for fast linear algebra calculations. """ -from warnings import warn from pathlib import Path +from warnings import warn + import numpy as np from pennylane_lightning.core.lightning_base import ( @@ -30,14 +31,14 @@ try: # pylint: disable=import-error, no-name-in-module from pennylane_lightning.lightning_qubit_ops import ( - allocate_aligned_array, - get_alignment, - best_alignment, MeasurementsC64, - StateVectorC64, MeasurementsC128, + StateVectorC64, StateVectorC128, + allocate_aligned_array, backend_info, + best_alignment, + get_alignment, ) LQ_CPP_BINARY_AVAILABLE = True @@ -45,34 +46,33 @@ LQ_CPP_BINARY_AVAILABLE = False if LQ_CPP_BINARY_AVAILABLE: - from typing import List from os import getenv + from typing import List + import pennylane as qml from pennylane import ( - math, BasisState, - StatePrep, - Projector, - Rot, DeviceError, + Projector, QuantumFunctionError, + Rot, + StatePrep, + math, ) + from pennylane.measurements import Expectation, MeasurementProcess, State from pennylane.operation import Tensor - from pennylane.measurements import MeasurementProcess, Expectation, State from pennylane.wires import Wires - import pennylane as qml - # pylint: disable=import-error, no-name-in-module, ungrouped-imports from pennylane_lightning.core._serialize import QuantumScriptSerializer from pennylane_lightning.core._version import __version__ from pennylane_lightning.lightning_qubit_ops.algorithms import ( AdjointJacobianC64, - create_ops_listC64, - VectorJacobianProductC64, AdjointJacobianC128, - create_ops_listC128, + VectorJacobianProductC64, VectorJacobianProductC128, + create_ops_listC64, + create_ops_listC128, ) def _state_dtype(dtype): diff --git a/requirements-dev.txt b/requirements-dev.txt index 7a0230999c..aee5243438 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -11,6 +11,7 @@ pre-commit>=2.19.0 black==23.7.0 clang-tidy~=16.0 clang-format~=16.0 +isort cmake custatevec-cu12 pylint diff --git a/tests/conftest.py b/tests/conftest.py index e10a3c203c..96531bf421 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -16,10 +16,10 @@ """ import os -import pytest import numpy as np - import pennylane as qml +import pytest + import pennylane_lightning # defaults diff --git a/tests/lightning_qubit/test_measurements_samples_MCMC.py b/tests/lightning_qubit/test_measurements_samples_MCMC.py index 376cd31fd2..e376294353 100644 --- a/tests/lightning_qubit/test_measurements_samples_MCMC.py +++ b/tests/lightning_qubit/test_measurements_samples_MCMC.py @@ -14,17 +14,13 @@ """ Unit tests for MCMC sampling in lightning.qubit. """ -import pytest -from conftest import LightningDevice # tested device - import numpy as np import pennylane as qml - import pytest +from conftest import LightningDevice # tested device from pennylane_lightning.lightning_qubit import LightningQubit - if not LightningQubit._CPP_BINARY_AVAILABLE: pytest.skip("No binary module found. Skipping.", allow_module_level=True) diff --git a/tests/test_adjoint_jacobian.py b/tests/test_adjoint_jacobian.py index 871a39e338..ad9db67239 100644 --- a/tests/test_adjoint_jacobian.py +++ b/tests/test_adjoint_jacobian.py @@ -15,16 +15,16 @@ Tests for ``adjoint_jacobian`` method on Lightning devices. """ import itertools -import pytest -from conftest import device_name, LightningDevice as ld - import math -from scipy.stats import unitary_group + import pennylane as qml +import pytest +from conftest import LightningDevice as ld +from conftest import device_name +from pennylane import QNode from pennylane import numpy as np -from pennylane import QNode, qnode -from pennylane import qchem - +from pennylane import qchem, qnode +from scipy.stats import unitary_group I, X, Y, Z = ( np.eye(2), diff --git a/tests/test_apply.py b/tests/test_apply.py index 9dd5861f99..5af7d4101a 100644 --- a/tests/test_apply.py +++ b/tests/test_apply.py @@ -15,12 +15,16 @@ Unit tests for Lightning devices. """ # pylint: disable=protected-access,cell-var-from-loop -import pytest -from conftest import THETA, PHI, TOL_STOCHASTIC, LightningDevice as ld, device_name - import math + import numpy as np import pennylane as qml + +# pylint: disable=protected-access,cell-var-from-loop +import pytest +from conftest import PHI, THETA, TOL_STOCHASTIC, VARPHI +from conftest import LightningDevice as ld +from conftest import device_name from pennylane import DeviceError from pennylane.operation import Operation from pennylane.wires import Wires diff --git a/tests/test_arrays.py b/tests/test_arrays.py index 2f3fccb8a7..c90d78026e 100644 --- a/tests/test_arrays.py +++ b/tests/test_arrays.py @@ -14,11 +14,10 @@ """ Array tests for Lightning devices. """ +import numpy as np import pytest from conftest import LightningDevice as ld -import numpy as np - try: from pennylane_lightning.lightning_qubit_ops import allocate_aligned_array except (ImportError, ModuleNotFoundError): diff --git a/tests/test_binary_info.py b/tests/test_binary_info.py index c6e269d3be..74d17c7875 100644 --- a/tests/test_binary_info.py +++ b/tests/test_binary_info.py @@ -14,17 +14,18 @@ """ Test binary information of Lightning devices. """ -import pytest import platform +import pytest + if platform.machine() != "x86_64": pytest.skip("Expected to fail on non x86 systems. Skipping.", allow_module_level=True) try: - from pennylane_lightning.lightning_qubit_ops import runtime_info, compile_info + from pennylane_lightning.lightning_qubit_ops import compile_info, runtime_info except (ImportError, ModuleNotFoundError): try: - from pennylane_lightning.lightning_kokkos_ops import runtime_info, compile_info + from pennylane_lightning.lightning_kokkos_ops import compile_info, runtime_info except (ImportError, ModuleNotFoundError): pytest.skip("No binary module found. Skipping.", allow_module_level=True) diff --git a/tests/test_comparison.py b/tests/test_comparison.py index bb6dfb935e..90bcc41128 100644 --- a/tests/test_comparison.py +++ b/tests/test_comparison.py @@ -15,13 +15,14 @@ Integration tests that compare the output states of the compiled Lightning device with the ``default.qubit``. """ -import pytest -from conftest import device_name, LightningDevice as ld - import itertools -import numpy as np import os + +import numpy as np import pennylane as qml +import pytest +from conftest import LightningDevice as ld +from conftest import device_name def lightning_backend_dev(wires): diff --git a/tests/test_decomposition.py b/tests/test_decomposition.py index a80e955cfd..29a7874871 100644 --- a/tests/test_decomposition.py +++ b/tests/test_decomposition.py @@ -14,11 +14,10 @@ """ Unit tests for operation decomposition with Lightning devices. """ -import pytest -from conftest import LightningDevice as ld - import numpy as np import pennylane as qml +import pytest +from conftest import LightningDevice as ld if not ld._CPP_BINARY_AVAILABLE: pytest.skip("No binary module found. Skipping.", allow_module_level=True) diff --git a/tests/test_device.py b/tests/test_device.py index 4039394276..98691b6fd1 100644 --- a/tests/test_device.py +++ b/tests/test_device.py @@ -14,11 +14,11 @@ """ Unit tests for Lightning devices creation. """ -import pytest -from conftest import device_name, LightningDevice as ld - import numpy as np import pennylane as qml +import pytest +from conftest import LightningDevice as ld +from conftest import device_name if not ld._CPP_BINARY_AVAILABLE: pytest.skip("No binary module found. Skipping.", allow_module_level=True) diff --git a/tests/test_execute.py b/tests/test_execute.py index 9efc2955dc..6e1db8ebb0 100644 --- a/tests/test_execute.py +++ b/tests/test_execute.py @@ -15,10 +15,10 @@ Integration tests for the ``execute`` method of Lightning devices. """ import functools -import pytest -from conftest import device_name import pennylane as qml +import pytest +from conftest import device_name from pennylane import numpy as np diff --git a/tests/test_expval.py b/tests/test_expval.py index 561a9ed235..95a985c362 100644 --- a/tests/test_expval.py +++ b/tests/test_expval.py @@ -15,11 +15,11 @@ Unit tests for the expval method of Lightning devices. """ import itertools -import pytest -from conftest import THETA, PHI, VARPHI, device_name import numpy as np import pennylane as qml +import pytest +from conftest import PHI, THETA, VARPHI, device_name @pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI))) diff --git a/tests/test_gates.py b/tests/test_gates.py index f0152a7ea1..847c3a845a 100644 --- a/tests/test_gates.py +++ b/tests/test_gates.py @@ -14,14 +14,13 @@ """ Unit tests for the correct application of gates with a Lightning device. """ -import pytest -from conftest import LightningDevice, device_name -from conftest import THETA, PHI - import copy import itertools + import numpy as np import pennylane as qml +import pytest +from conftest import PHI, THETA, LightningDevice, device_name @pytest.fixture diff --git a/tests/test_measurements.py b/tests/test_measurements.py index edc4b59fa6..62960f92fd 100644 --- a/tests/test_measurements.py +++ b/tests/test_measurements.py @@ -14,17 +14,14 @@ """ Unit tests for Measurements in Lightning devices. """ -import pytest -from conftest import device_name, LightningDevice as ld, lightning_ops - -import numpy as np import math +import numpy as np import pennylane as qml -from pennylane.measurements import ( - Variance, - Expectation, -) +import pytest +from conftest import LightningDevice as ld +from conftest import device_name, lightning_ops +from pennylane.measurements import Expectation, Variance if not ld._CPP_BINARY_AVAILABLE: pytest.skip("No binary module found. Skipping.", allow_module_level=True) diff --git a/tests/test_measurements_sparse.py b/tests/test_measurements_sparse.py index 4849c9a43e..f01e6dd711 100644 --- a/tests/test_measurements_sparse.py +++ b/tests/test_measurements_sparse.py @@ -14,11 +14,11 @@ """ Unit tests for Sparse Measurements Lightning devices. """ -import pytest -from conftest import device_name, LightningDevice as ld - import numpy as np import pennylane as qml +import pytest +from conftest import LightningDevice as ld +from conftest import device_name from pennylane import qchem if not ld._CPP_BINARY_AVAILABLE: diff --git a/tests/test_serialize.py b/tests/test_serialize.py index e81f5c1783..1f726a55ef 100644 --- a/tests/test_serialize.py +++ b/tests/test_serialize.py @@ -14,54 +14,57 @@ """ Unit tests for the serialization helper functions. """ -import pytest -from conftest import device_name, LightningDevice - import numpy as np import pennylane as qml -from pennylane_lightning.core._serialize import QuantumScriptSerializer, global_phase_diagonal +import pytest +from conftest import LightningDevice, device_name + +from pennylane_lightning.core._serialize import ( + QuantumScriptSerializer, + global_phase_diagonal, +) if not LightningDevice._CPP_BINARY_AVAILABLE: pytest.skip("No binary module found. Skipping.", allow_module_level=True) if device_name == "lightning.kokkos": from pennylane_lightning.lightning_kokkos_ops.observables import ( - NamedObsC64, - NamedObsC128, - HermitianObsC64, - HermitianObsC128, - TensorProdObsC64, - TensorProdObsC128, HamiltonianC64, HamiltonianC128, + HermitianObsC64, + HermitianObsC128, + NamedObsC64, + NamedObsC128, SparseHamiltonianC64, SparseHamiltonianC128, + TensorProdObsC64, + TensorProdObsC128, ) elif device_name == "lightning.gpu": from pennylane_lightning.lightning_gpu_ops.observables import ( - NamedObsC64, - NamedObsC128, - HermitianObsC64, - HermitianObsC128, - TensorProdObsC64, - TensorProdObsC128, HamiltonianC64, HamiltonianC128, + HermitianObsC64, + HermitianObsC128, + NamedObsC64, + NamedObsC128, SparseHamiltonianC64, SparseHamiltonianC128, + TensorProdObsC64, + TensorProdObsC128, ) else: from pennylane_lightning.lightning_qubit_ops.observables import ( - NamedObsC64, - NamedObsC128, - HermitianObsC64, - HermitianObsC128, - TensorProdObsC64, - TensorProdObsC128, HamiltonianC64, HamiltonianC128, + HermitianObsC64, + HermitianObsC128, + NamedObsC64, + NamedObsC128, SparseHamiltonianC64, SparseHamiltonianC128, + TensorProdObsC64, + TensorProdObsC128, ) diff --git a/tests/test_serialize_chunk_obs.py b/tests/test_serialize_chunk_obs.py index ec70b998c0..58562657c1 100644 --- a/tests/test_serialize_chunk_obs.py +++ b/tests/test_serialize_chunk_obs.py @@ -14,13 +14,13 @@ """ Unit tests for the serialization helper functions. """ +import numpy as np +import pennylane as qml import pytest -from conftest import device_name, LightningDevice as ld +from conftest import LightningDevice as ld +from conftest import device_name -import pennylane as qml -import numpy as np import pennylane_lightning - from pennylane_lightning.core._serialize import QuantumScriptSerializer if not ld._CPP_BINARY_AVAILABLE: diff --git a/tests/test_serialize_no_binaries.py b/tests/test_serialize_no_binaries.py index f15b53ea67..28f72fcb14 100644 --- a/tests/test_serialize_no_binaries.py +++ b/tests/test_serialize_no_binaries.py @@ -15,7 +15,7 @@ Unit tests for the serialization helper functions. """ import pytest -from conftest import device_name, LightningDevice +from conftest import LightningDevice, device_name from pennylane_lightning.core._serialize import QuantumScriptSerializer diff --git a/tests/test_var.py b/tests/test_var.py index ccee5cda20..bf0779da6f 100644 --- a/tests/test_var.py +++ b/tests/test_var.py @@ -14,11 +14,10 @@ """ Unit tests for the var method of the :mod:`pennylane_lightning.LightningQubit` device. """ -import pytest -from conftest import THETA, PHI, VARPHI - import numpy as np import pennylane as qml +import pytest +from conftest import PHI, THETA, VARPHI np.random.seed(42) diff --git a/tests/test_vjp.py b/tests/test_vjp.py index b4e28570db..d53b9b4135 100644 --- a/tests/test_vjp.py +++ b/tests/test_vjp.py @@ -14,11 +14,12 @@ """ Tests for the ``vjp`` method of LightningKokkos. """ -import pytest -from conftest import device_name, LightningDevice as ld - import math + import pennylane as qml +import pytest +from conftest import LightningDevice as ld +from conftest import device_name from pennylane import numpy as np if not ld._CPP_BINARY_AVAILABLE: From 4ecf2fbaa9174a03841c1fffdd9c065aabc5d66e Mon Sep 17 00:00:00 2001 From: Dev version update bot Date: Tue, 5 Mar 2024 14:59:58 +0000 Subject: [PATCH 102/428] Auto update version --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index 512bfe6499..5ef6d062a5 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.36.0-dev1" +__version__ = "0.36.0-dev2" From 3bab48bd1097037bf1621c8594bb1925c5221cef Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Tue, 5 Mar 2024 10:05:45 -0500 Subject: [PATCH 103/428] isort --- pennylane_lightning/lightning_qubit/_measurements.py | 5 ++++- tests/lightning_qubit/test_measurements_class.py | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/_measurements.py b/pennylane_lightning/lightning_qubit/_measurements.py index e5102a7e40..ad2fe74a57 100644 --- a/pennylane_lightning/lightning_qubit/_measurements.py +++ b/pennylane_lightning/lightning_qubit/_measurements.py @@ -17,7 +17,10 @@ # pylint: disable=import-error, no-name-in-module, ungrouped-imports try: - from pennylane_lightning.lightning_qubit_ops import MeasurementsC64, MeasurementsC128 + from pennylane_lightning.lightning_qubit_ops import ( + MeasurementsC64, + MeasurementsC128, + ) except ImportError: pass diff --git a/tests/lightning_qubit/test_measurements_class.py b/tests/lightning_qubit/test_measurements_class.py index a22c129e32..1c4cbf3753 100644 --- a/tests/lightning_qubit/test_measurements_class.py +++ b/tests/lightning_qubit/test_measurements_class.py @@ -25,7 +25,10 @@ from pennylane.measurements import ProbabilityMP, VarianceMP try: - from pennylane_lightning.lightning_qubit_ops import MeasurementsC64, MeasurementsC128 + from pennylane_lightning.lightning_qubit_ops import ( + MeasurementsC64, + MeasurementsC128, + ) except ImportError: pass From 11f6a1b048fa2252467faad761b97ff83b7093c5 Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Mon, 4 Mar 2024 19:48:31 +0000 Subject: [PATCH 104/428] Add qml.var support. --- pennylane_lightning/lightning_qubit/_measurements.py | 3 +++ tests/lightning_qubit/test_measurements_class.py | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/pennylane_lightning/lightning_qubit/_measurements.py b/pennylane_lightning/lightning_qubit/_measurements.py index ad2fe74a57..467807722c 100644 --- a/pennylane_lightning/lightning_qubit/_measurements.py +++ b/pennylane_lightning/lightning_qubit/_measurements.py @@ -208,9 +208,12 @@ def get_measurement_function( return self.state_diagonalizing_gates return self.expval +<<<<<<< HEAD if isinstance(measurementprocess, ProbabilityMP): return self.probs +======= +>>>>>>> c1c03954 (Add qml.var support.) if isinstance(measurementprocess, VarianceMP): if measurementprocess.obs.name in [ "Identity", diff --git a/tests/lightning_qubit/test_measurements_class.py b/tests/lightning_qubit/test_measurements_class.py index 1c4cbf3753..f2718b6163 100644 --- a/tests/lightning_qubit/test_measurements_class.py +++ b/tests/lightning_qubit/test_measurements_class.py @@ -97,6 +97,10 @@ def test_only_support_state_measurements(self, lightning_sv): @pytest.mark.parametrize( "mp", ( +<<<<<<< HEAD +======= + qml.probs(wires=0), +>>>>>>> c1c03954 (Add qml.var support.) qml.vn_entropy(wires=0), CustomStateMeasurement(), qml.expval(qml.Identity(0)), From 186e53b05b0c24b5fee78796cf9c9e0c19f12fad Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Tue, 5 Mar 2024 01:25:02 +0000 Subject: [PATCH 105/428] Add probs support. --- pennylane_lightning/lightning_qubit/_measurements.py | 6 ++++++ tests/lightning_qubit/test_measurements_class.py | 4 ---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/_measurements.py b/pennylane_lightning/lightning_qubit/_measurements.py index 467807722c..2725091fc0 100644 --- a/pennylane_lightning/lightning_qubit/_measurements.py +++ b/pennylane_lightning/lightning_qubit/_measurements.py @@ -208,12 +208,18 @@ def get_measurement_function( return self.state_diagonalizing_gates return self.expval +<<<<<<< HEAD <<<<<<< HEAD if isinstance(measurementprocess, ProbabilityMP): return self.probs ======= >>>>>>> c1c03954 (Add qml.var support.) +======= + if isinstance(measurementprocess, ProbabilityMP): + return self.probs + +>>>>>>> 383efdb1 (Add probs support.) if isinstance(measurementprocess, VarianceMP): if measurementprocess.obs.name in [ "Identity", diff --git a/tests/lightning_qubit/test_measurements_class.py b/tests/lightning_qubit/test_measurements_class.py index f2718b6163..1c4cbf3753 100644 --- a/tests/lightning_qubit/test_measurements_class.py +++ b/tests/lightning_qubit/test_measurements_class.py @@ -97,10 +97,6 @@ def test_only_support_state_measurements(self, lightning_sv): @pytest.mark.parametrize( "mp", ( -<<<<<<< HEAD -======= - qml.probs(wires=0), ->>>>>>> c1c03954 (Add qml.var support.) qml.vn_entropy(wires=0), CustomStateMeasurement(), qml.expval(qml.Identity(0)), From 0222336d2a3869ab3c134d509c1eb2099d6f324f Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Tue, 5 Mar 2024 15:34:22 +0000 Subject: [PATCH 106/428] Add measurement tests with wires. --- .../lightning_gpu/lightning_gpu.py | 14 +++++++------- .../lightning_qubit/_measurements.py | 11 +---------- .../test_measurements_class.py | 19 ++++++++++++++----- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/pennylane_lightning/lightning_gpu/lightning_gpu.py b/pennylane_lightning/lightning_gpu/lightning_gpu.py index be36ff548d..1ff0642620 100644 --- a/pennylane_lightning/lightning_gpu/lightning_gpu.py +++ b/pennylane_lightning/lightning_gpu/lightning_gpu.py @@ -91,6 +91,13 @@ from pennylane.ops.op_math import Adjoint from pennylane.wires import Wires + # pylint: disable=import-error, no-name-in-module, ungrouped-imports + from pennylane_lightning.core._serialize import ( + QuantumScriptSerializer, + global_phase_diagonal, + ) + from pennylane_lightning.core._version import __version__ + # pylint: disable=no-name-in-module, ungrouped-imports from pennylane_lightning.lightning_gpu_ops.algorithms import ( AdjointJacobianC64, @@ -99,13 +106,6 @@ create_ops_listC128, ) - # pylint: disable=import-error, no-name-in-module, ungrouped-imports - from pennylane_lightning.core._serialize import ( - QuantumScriptSerializer, - global_phase_diagonal, - ) - from pennylane_lightning.core._version import __version__ - if MPI_SUPPORT: from pennylane_lightning.lightning_gpu_ops.algorithmsMPI import ( AdjointJacobianMPIC64, diff --git a/pennylane_lightning/lightning_qubit/_measurements.py b/pennylane_lightning/lightning_qubit/_measurements.py index 2725091fc0..8a6b083020 100644 --- a/pennylane_lightning/lightning_qubit/_measurements.py +++ b/pennylane_lightning/lightning_qubit/_measurements.py @@ -30,8 +30,8 @@ import pennylane as qml from pennylane.measurements import ( ExpectationMP, - ProbabilityMP, MeasurementProcess, + ProbabilityMP, StateMeasurement, VarianceMP, ) @@ -208,18 +208,9 @@ def get_measurement_function( return self.state_diagonalizing_gates return self.expval -<<<<<<< HEAD -<<<<<<< HEAD - if isinstance(measurementprocess, ProbabilityMP): - return self.probs - -======= ->>>>>>> c1c03954 (Add qml.var support.) -======= if isinstance(measurementprocess, ProbabilityMP): return self.probs ->>>>>>> 383efdb1 (Add probs support.) if isinstance(measurementprocess, VarianceMP): if measurementprocess.obs.name in [ "Identity", diff --git a/tests/lightning_qubit/test_measurements_class.py b/tests/lightning_qubit/test_measurements_class.py index 1c4cbf3753..faa8dedef0 100644 --- a/tests/lightning_qubit/test_measurements_class.py +++ b/tests/lightning_qubit/test_measurements_class.py @@ -14,15 +14,15 @@ import itertools import math - from typing import Sequence + import numpy as np import pennylane as qml import pytest from conftest import LightningDevice # tested device from pennylane.devices import DefaultQubit -from scipy.sparse import csr_matrix, random_array from pennylane.measurements import ProbabilityMP, VarianceMP +from scipy.sparse import csr_matrix, random_array try: from pennylane_lightning.lightning_qubit_ops import ( @@ -424,6 +424,8 @@ def calculate_reference(tape, lightning_sv): @pytest.mark.parametrize( "observable", ( + [0], + [1, 2], qml.PauliX(0), qml.PauliY(1), qml.PauliZ(2), @@ -444,13 +446,19 @@ def test_single_return_value(self, measurement, observable, lightning_sv, tol): (qml.ops.Sum, qml.ops.SProd, qml.ops.Prod, qml.Hamiltonian, qml.SparseHamiltonian), ): return + if measurement is not qml.probs and isinstance(observable, list): + return n_qubits = 4 n_layers = 1 np.random.seed(0) weights = np.random.rand(n_layers, n_qubits, 3) ops = [qml.Hadamard(i) for i in range(n_qubits)] ops += [qml.StronglyEntanglingLayers(weights, wires=range(n_qubits))] - measurements = [measurement(op=observable)] + measurements = ( + [measurement(wires=observable)] + if isinstance(observable, list) + else [measurement(op=observable)] + ) tape = qml.tape.QuantumScript(ops, measurements) expected = self.calculate_reference(tape, lightning_sv) @@ -459,7 +467,8 @@ def test_single_return_value(self, measurement, observable, lightning_sv, tol): m = LightningMeasurements(statevector) result = m.measure_final_state(tape) - assert np.allclose(result, expected, tol) + # a few tests may fail in single precision, and hence we increase the tolerance + assert np.allclose(result, expected, max(tol, 1.0e-5)) @pytest.mark.parametrize("measurement", [qml.expval, qml.probs, qml.var]) @pytest.mark.parametrize( @@ -526,7 +535,7 @@ def test_double_return_value(self, measurement, obs0_, obs1_, lightning_sv, tol) assert isinstance(result, Sequence) assert len(result) == len(expected) - # a few tests fail in single precision, and hence we increase the tolerance + # a few tests may fail in single precision, and hence we increase the tolerance for r, e in zip(result, expected): assert np.allclose(r, e, max(tol, 1.0e-5)) From bdc28f80c7378161f0638ec7cb672ebf075a5586 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Tue, 5 Mar 2024 13:41:18 -0500 Subject: [PATCH 107/428] review suggestions --- .../lightning_qubit/_measurements.py | 2 +- .../lightning_qubit/_state_vector.py | 80 ++++++------------- .../test_measurements_class.py | 15 +--- .../test_state_vector_class.py | 11 +-- 4 files changed, 28 insertions(+), 80 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/_measurements.py b/pennylane_lightning/lightning_qubit/_measurements.py index 79c40d681e..efe38a1758 100644 --- a/pennylane_lightning/lightning_qubit/_measurements.py +++ b/pennylane_lightning/lightning_qubit/_measurements.py @@ -43,7 +43,7 @@ class LightningMeasurements: Measures the state provided by the LightningStateVector class. Args: - qubit_state(LightningStateVector) + qubit_state(LightningStateVector): Lightning state-vector class containing the state vector to be measured. """ def __init__(self, qubit_state: LightningStateVector) -> None: diff --git a/pennylane_lightning/lightning_qubit/_state_vector.py b/pennylane_lightning/lightning_qubit/_state_vector.py index f33054c7da..6839d0aa6a 100644 --- a/pennylane_lightning/lightning_qubit/_state_vector.py +++ b/pennylane_lightning/lightning_qubit/_state_vector.py @@ -43,7 +43,7 @@ class LightningStateVector: Args: num_wires(int): the number of wires to initialize the device with dtype: Datatypes for state-vector representation. Must be one of - ``np.complex64`` or ``np.complex128``. + ``np.complex64`` or ``np.complex128``. Default is ``np.complex128`` device_name(string): state vector device name. Options: ["lightning.qubit"] """ @@ -81,6 +81,26 @@ def num_wires(self): """Number of wires addressed on this device""" return self._num_wires + @property + def state_vector(self): + """Returns a handle to the state vector.""" + return self._qubit_state + + @property + def state(self): + """Copy the state vector data to a numpy array. + + **Example** + + >>> dev = qml.device('lightning.qubit', wires=1) + >>> dev.apply([qml.PauliX(wires=[0])]) + >>> print(dev.state) + [0.+0.j 1.+0.j] + """ + state = np.zeros(2**self._num_wires, dtype=self.dtype) + self._qubit_state.getState(state) + return state + def _state_dtype(self): """Binding to Lightning Managed state vector C++ class. @@ -88,42 +108,10 @@ def _state_dtype(self): """ return StateVectorC128 if self.dtype == np.complex128 else StateVectorC64 - @staticmethod - def _asarray(arr, dtype=None): - """Verify data alignment and allocate aligned memory if needed. - - Args: - arr (numpy.array): data array - dtype (dtype, optional): if provided will convert the array data type. - - Returns: - np.array: numpy array with aligned data. - """ - arr = np.asarray(arr) # arr is not copied - - if arr.dtype.kind not in ["f", "c"]: - return arr - - if not dtype: - dtype = arr.dtype - - # We allocate a new aligned memory and copy data to there if alignment or dtype - # mismatches - # Note that get_alignment does not necessarily return CPUMemoryModel(Unaligned) - # numpy allocated memory as the memory location happens to be aligned. - if int(get_alignment(arr)) < int(best_alignment()) or arr.dtype != dtype: - new_arr = allocate_aligned_array(arr.size, np.dtype(dtype), False).reshape(arr.shape) - if len(arr.shape): - new_arr[:] = arr - else: - np.copyto(new_arr, arr) - arr = new_arr - return arr - def _create_basis_state(self, index): """Return a computational basis state over all wires. + Args: - _qubit_state: a handle to Lightning qubit state. index (int): integer representing the computational basis state. """ self._qubit_state.setBasisState(index) @@ -133,27 +121,6 @@ def reset_state(self): # init the state vector to |00..0> self._qubit_state.resetStateVector() - @property - def state(self): - """Copy the state vector data to a numpy array. - - **Example** - - >>> dev = qml.device('lightning.qubit', wires=1) - >>> dev.apply([qml.PauliX(wires=[0])]) - >>> print(dev.state) - [0.+0.j 1.+0.j] - """ - state = np.zeros(2**self._num_wires, dtype=self.dtype) - state = self._asarray(state, dtype=self.dtype) - self._qubit_state.getState(state) - return state - - @property - def state_vector(self): - """Returns a handle to the state vector.""" - return self._qubit_state - def _preprocess_state_vector(self, state, device_wires): """Initialize the internal state vector in a specified state. @@ -163,14 +130,13 @@ def _preprocess_state_vector(self, state, device_wires): device_wires (Wires): wires that get initialized in the state Returns: + array[int]: indices for which the state is changed to input state vector elements array[complex]: normalized input state of length ``2**len(wires)`` or broadcasted state of shape ``(batch_size, 2**len(wires))`` - array[int]: indices for which the state is changed to input state vector elements """ # special case for integral types if state.dtype.kind == "i": state = np.array(state, dtype=self.dtype) - state = self._asarray(state, dtype=self.dtype) if len(device_wires) == self._num_wires and Wires(sorted(device_wires)) == device_wires: return None, state diff --git a/tests/lightning_qubit/test_measurements_class.py b/tests/lightning_qubit/test_measurements_class.py index ff951385f9..ccf59eff32 100644 --- a/tests/lightning_qubit/test_measurements_class.py +++ b/tests/lightning_qubit/test_measurements_class.py @@ -21,14 +21,6 @@ from conftest import LightningDevice # tested device from pennylane.devices import DefaultQubit -try: - from pennylane_lightning.lightning_qubit_ops import ( - MeasurementsC64, - MeasurementsC128, - ) -except ImportError: - pass - from pennylane_lightning.lightning_qubit import LightningQubit from pennylane_lightning.lightning_qubit._measurements import LightningMeasurements from pennylane_lightning.lightning_qubit._state_vector import LightningStateVector @@ -39,6 +31,9 @@ if LightningDevice != LightningQubit: pytest.skip("Exclusive tests for lightning.qubit. Skipping.", allow_module_level=True) +THETA = np.linspace(0.11, 1, 3) +PHI = np.linspace(0.32, 1, 3) + # General LightningStateVector fixture, for any number of wires. @pytest.fixture( @@ -190,10 +185,6 @@ def test_state_vector_projector_expval(self, lightning_sv, method_name): assert qml.math.allclose(result, np.sin(phi / 2) ** 2) -THETA = np.linspace(0.11, 1, 3) -PHI = np.linspace(0.32, 1, 3) - - @pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI))) class TestExpval: """Test expectation value calculations.""" diff --git a/tests/lightning_qubit/test_state_vector_class.py b/tests/lightning_qubit/test_state_vector_class.py index 11a295e7c3..63e8cd2114 100644 --- a/tests/lightning_qubit/test_state_vector_class.py +++ b/tests/lightning_qubit/test_state_vector_class.py @@ -58,15 +58,6 @@ def test_wrong_dtype(dtype): assert LightningStateVector(3, dtype=dtype) -@pytest.mark.parametrize("dtype", [np.double, np.complex64, None]) -@pytest.mark.parametrize("data", [1.0, [1.0], [1.0, 2.0]]) -def test_asarray(dtype, data, tol): - """Test _asarray returns the right values""" - wires = 2 - state_vector = LightningStateVector(wires) - assert np.allclose(data, state_vector._asarray(data, dtype), atol=tol) - - def test_errors_basis_state(): with pytest.raises(ValueError, match="BasisState parameter must consist of 0 or 1 integers."): state_vector = LightningStateVector(2) @@ -139,7 +130,7 @@ def test_reset_state(tol, operation, par): state_vector.reset_state() - expected_output = state_vector._asarray([1, 0, 0, 0]) + expected_output = np.array([1, 0, 0, 0], dtype=state_vector.dtype) assert np.allclose(state_vector.state, expected_output, atol=tol, rtol=0) From 45b5448dff1911ecd4719645117c341e7c00919b Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Tue, 5 Mar 2024 13:47:46 -0500 Subject: [PATCH 108/428] remove unused imports --- pennylane_lightning/lightning_qubit/_state_vector.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/_state_vector.py b/pennylane_lightning/lightning_qubit/_state_vector.py index 6839d0aa6a..c91f65f697 100644 --- a/pennylane_lightning/lightning_qubit/_state_vector.py +++ b/pennylane_lightning/lightning_qubit/_state_vector.py @@ -20,8 +20,6 @@ StateVectorC64, StateVectorC128, allocate_aligned_array, - best_alignment, - get_alignment, ) except ImportError: pass From 25374dd7c8687947e495d853f401ba6e4cab5f63 Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Tue, 5 Mar 2024 19:32:53 +0000 Subject: [PATCH 109/428] Introduce _new_API and fix/skip few tests. --- pennylane_lightning/core/_serialize.py | 2 +- pennylane_lightning/core/lightning_base.py | 5 ++- .../lightning_gpu/lightning_gpu.py | 16 ++++---- .../lightning_kokkos/lightning_kokkos.py | 12 +++--- .../lightning_qubit/lightning_qubit2.py | 38 ++++++++++++++----- pennylane_lightning/lightning_qubit2 | 1 + tests/conftest.py | 7 +++- .../test_measurements_class.py | 32 ++++++++++++++-- tests/lightning_qubit2/test_expval_2.py | 7 ++-- tests/test_apply.py | 4 ++ tests/test_decomposition.py | 1 + tests/test_device.py | 2 +- tests/test_expval.py | 4 +- tests/test_gates.py | 9 +++-- tests/test_measurements.py | 17 +++++++-- tests/test_var.py | 4 +- 16 files changed, 116 insertions(+), 45 deletions(-) create mode 120000 pennylane_lightning/lightning_qubit2 diff --git a/pennylane_lightning/core/_serialize.py b/pennylane_lightning/core/_serialize.py index 2c85333b05..2eb0078aa5 100644 --- a/pennylane_lightning/core/_serialize.py +++ b/pennylane_lightning/core/_serialize.py @@ -60,7 +60,7 @@ def __init__( self.use_csingle = use_csingle self.device_name = device_name self.split_obs = split_obs - if device_name == "lightning.qubit": + if device_name in ("lightning.qubit", "lightning.qubit2"): try: import pennylane_lightning.lightning_qubit_ops as lightning_ops except ImportError as exception: diff --git a/pennylane_lightning/core/lightning_base.py b/pennylane_lightning/core/lightning_base.py index 36fa4c504f..61876ac766 100644 --- a/pennylane_lightning/core/lightning_base.py +++ b/pennylane_lightning/core/lightning_base.py @@ -60,6 +60,7 @@ class LightningBase(QubitDevice): author = "Xanadu Inc." short_name = "lightning.base" _CPP_BINARY_AVAILABLE = True + _new_API = False def __init__( self, @@ -76,7 +77,7 @@ def __init__( r_dtype = np.float64 self.use_csingle = False else: - raise TypeError(f"Unsupported complex Type: {c_dtype}") + raise TypeError(f"Unsupported complex type: {c_dtype}") super().__init__(wires, shots=shots, r_dtype=r_dtype, c_dtype=c_dtype) self._batch_obs = batch_obs @@ -403,7 +404,7 @@ def __init__(self, wires, *, c_dtype=np.complex128, **kwargs): elif c_dtype is np.complex128: r_dtype = np.float64 else: - raise TypeError(f"Unsupported complex Type: {c_dtype}") + raise TypeError(f"Unsupported complex type: {c_dtype}") super().__init__(wires, r_dtype=r_dtype, c_dtype=c_dtype, **kwargs) @property diff --git a/pennylane_lightning/lightning_gpu/lightning_gpu.py b/pennylane_lightning/lightning_gpu/lightning_gpu.py index 1ff0642620..1a5a45798b 100644 --- a/pennylane_lightning/lightning_gpu/lightning_gpu.py +++ b/pennylane_lightning/lightning_gpu/lightning_gpu.py @@ -91,13 +91,6 @@ from pennylane.ops.op_math import Adjoint from pennylane.wires import Wires - # pylint: disable=import-error, no-name-in-module, ungrouped-imports - from pennylane_lightning.core._serialize import ( - QuantumScriptSerializer, - global_phase_diagonal, - ) - from pennylane_lightning.core._version import __version__ - # pylint: disable=no-name-in-module, ungrouped-imports from pennylane_lightning.lightning_gpu_ops.algorithms import ( AdjointJacobianC64, @@ -106,6 +99,13 @@ create_ops_listC128, ) + # pylint: disable=import-error, no-name-in-module, ungrouped-imports + from pennylane_lightning.core._serialize import ( + QuantumScriptSerializer, + global_phase_diagonal, + ) + from pennylane_lightning.core._version import __version__ + if MPI_SUPPORT: from pennylane_lightning.lightning_gpu_ops.algorithmsMPI import ( AdjointJacobianMPIC64, @@ -261,7 +261,7 @@ def __init__( elif c_dtype is np.complex128: self.use_csingle = False else: - raise TypeError(f"Unsupported complex Type: {c_dtype}") + raise TypeError(f"Unsupported complex type: {c_dtype}") super().__init__(wires, shots=shots, c_dtype=c_dtype) diff --git a/pennylane_lightning/lightning_kokkos/lightning_kokkos.py b/pennylane_lightning/lightning_kokkos/lightning_kokkos.py index 9844d86ec9..90c53cefe7 100644 --- a/pennylane_lightning/lightning_kokkos/lightning_kokkos.py +++ b/pennylane_lightning/lightning_kokkos/lightning_kokkos.py @@ -63,6 +63,12 @@ from pennylane.operation import Tensor from pennylane.ops.op_math import Adjoint from pennylane.wires import Wires + from pennylane_lightning.lightning_kokkos_ops.algorithms import ( + AdjointJacobianC64, + AdjointJacobianC128, + create_ops_listC64, + create_ops_listC128, + ) # pylint: disable=import-error, no-name-in-module, ungrouped-imports from pennylane_lightning.core._serialize import ( @@ -70,12 +76,6 @@ global_phase_diagonal, ) from pennylane_lightning.core._version import __version__ - from pennylane_lightning.lightning_kokkos_ops.algorithms import ( - AdjointJacobianC64, - AdjointJacobianC128, - create_ops_listC64, - create_ops_listC128, - ) def _kokkos_dtype(dtype): if dtype not in [np.complex128, np.complex64]: # pragma: no cover diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index 03fcde440c..47dc9f06be 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -15,31 +15,31 @@ This module contains the LightningQubit2 class that inherits from the new device interface. """ -from typing import Optional, Union, Sequence, Callable from dataclasses import replace -import numpy as np +from pathlib import Path +from typing import Callable, Optional, Sequence, Union +import numpy as np import pennylane as qml -from pennylane.devices import Device, ExecutionConfig, DefaultExecutionConfig -from pennylane.devices.modifiers import single_tape_support, simulator_tracking +from pennylane.devices import DefaultExecutionConfig, Device, ExecutionConfig +from pennylane.devices.modifiers import simulator_tracking, single_tape_support from pennylane.devices.preprocess import ( decompose, + no_sampling, validate_device_wires, - decompose, validate_measurements, validate_observables, - no_sampling, ) -from pennylane.tape import QuantumTape, QuantumScript +from pennylane.tape import QuantumScript, QuantumTape from pennylane.transforms.core import TransformProgram from pennylane.typing import Result, ResultBatch -from ._state_vector import LightningStateVector from ._measurements import LightningMeasurements +from ._state_vector import LightningStateVector try: # pylint: disable=import-error, unused-import - import pennylane_lightning.lightning_qubit_ops + from pennylane_lightning.lightning_qubit_ops import backend_info LQ_CPP_BINARY_AVAILABLE = True except ImportError: @@ -158,6 +158,8 @@ def simulate_and_jacobian(circuit: QuantumTape): "QFT", "ECR", "BlockEncode", + "GlobalPhase", + "C(GlobalPhase)", } ) """The set of supported operations.""" @@ -199,7 +201,13 @@ class LightningQubit2(Device): _device_options = ("rng", "c_dtype", "batch_obs", "mcmc", "kernel_name", "num_burnin") + _new_API = True _CPP_BINARY_AVAILABLE = LQ_CPP_BINARY_AVAILABLE + short_name = "lightning.qubit2" + operations = _operations + observables = _observables + _backend_info = backend_info + config = Path(__file__).parent / "lightning_qubit.toml" def __init__( # pylint: disable=too-many-arguments self, @@ -251,16 +259,28 @@ def c_dtype(self): """State vector complex data type.""" return self._c_dtype + @property + def C_DTYPE(self): + """State vector complex data type.""" + return self._c_dtype + + @classmethod @property def operations(self) -> frozenset[str]: """The names of the supported operations.""" return _operations + @classmethod @property def observables(self) -> frozenset[str]: """The names of the supported observables.""" return _observables + @property + def state(self): + """Returns a copy of the state vector data in a NumPy array.""" + return self._statevector.state + def _setup_execution_config(self, config): """ Update the execution config with choices for how the device should be used and the device options. diff --git a/pennylane_lightning/lightning_qubit2 b/pennylane_lightning/lightning_qubit2 new file mode 120000 index 0000000000..0bcda1f466 --- /dev/null +++ b/pennylane_lightning/lightning_qubit2 @@ -0,0 +1 @@ +lightning_qubit \ No newline at end of file diff --git a/tests/conftest.py b/tests/conftest.py index 96531bf421..cbc0d29dde 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -87,7 +87,7 @@ def n_subsystems(request): # Looking for the device for testing. default_device = "lightning.qubit" -supported_devices = {"lightning.kokkos", "lightning.qubit", "lightning.gpu"} +supported_devices = {"lightning.kokkos", "lightning.qubit", "lightning.qubit2", "lightning.gpu"} supported_devices.update({sb.replace(".", "_") for sb in supported_devices}) @@ -131,6 +131,11 @@ def get_device(): if hasattr(pennylane_lightning, "lightning_gpu_ops"): import pennylane_lightning.lightning_gpu_ops as lightning_ops +elif device_name == "lightning.qubit2": + from pennylane_lightning.lightning_qubit2 import LightningQubit2 as LightningDevice + + if hasattr(pennylane_lightning, "lightning_qubit_ops"): + import pennylane_lightning.lightning_qubit_ops as lightning_ops else: from pennylane_lightning.lightning_qubit import LightningQubit as LightningDevice diff --git a/tests/lightning_qubit/test_measurements_class.py b/tests/lightning_qubit/test_measurements_class.py index faa8dedef0..838ba70e1a 100644 --- a/tests/lightning_qubit/test_measurements_class.py +++ b/tests/lightning_qubit/test_measurements_class.py @@ -19,7 +19,7 @@ import numpy as np import pennylane as qml import pytest -from conftest import LightningDevice # tested device +from conftest import LightningDevice, device_name # tested device from pennylane.devices import DefaultQubit from pennylane.measurements import ProbabilityMP, VarianceMP from scipy.sparse import csr_matrix, random_array @@ -32,14 +32,14 @@ except ImportError: pass -from pennylane_lightning.lightning_qubit import LightningQubit +from pennylane_lightning.lightning_qubit2 import LightningQubit2 from pennylane_lightning.lightning_qubit._measurements import LightningMeasurements from pennylane_lightning.lightning_qubit._state_vector import LightningStateVector -if not LightningQubit._CPP_BINARY_AVAILABLE: +if not LightningDevice._CPP_BINARY_AVAILABLE: pytest.skip("No binary module found. Skipping.", allow_module_level=True) -if LightningDevice != LightningQubit: +if LightningDevice != LightningQubit2: pytest.skip("Exclusive tests for lightning.qubit. Skipping.", allow_module_level=True) @@ -426,6 +426,7 @@ def calculate_reference(tape, lightning_sv): ( [0], [1, 2], + [1, 0], qml.PauliX(0), qml.PauliY(1), qml.PauliZ(2), @@ -539,6 +540,29 @@ def test_double_return_value(self, measurement, obs0_, obs1_, lightning_sv, tol) for r, e in zip(result, expected): assert np.allclose(r, e, max(tol, 1.0e-5)) + @pytest.mark.parametrize( + "cases", + [ + [[0, 1], [1, 0]], + [[1, 0], [0, 1]], + ], + ) + def test_probs_tape_unordered_wires(self, cases, tol): + """Test probs with a circuit on wires=[0] fails for out-of-order wires passed to probs.""" + + x, y, z = [0.5, 0.3, -0.7] + dev = qml.device(device_name, wires=cases[1]) + + def circuit(): + qml.RX(0.4, wires=[0]) + qml.Rot(x, y, z, wires=[0]) + qml.RY(-0.2, wires=[0]) + return qml.probs(wires=cases[0]) + + expected = qml.QNode(circuit, qml.device("default.qubit", wires=cases[1]))() + results = qml.QNode(circuit, dev)() + assert np.allclose(expected, results, tol) + class TestControlledOps: """Tests for controlled operations""" diff --git a/tests/lightning_qubit2/test_expval_2.py b/tests/lightning_qubit2/test_expval_2.py index 1349e02b94..6952c49e95 100644 --- a/tests/lightning_qubit2/test_expval_2.py +++ b/tests/lightning_qubit2/test_expval_2.py @@ -14,14 +14,13 @@ """ Tests for process and execute (expval calculation). """ -import pytest - import numpy as np import pennylane as qml -from pennylane_lightning.lightning_qubit import LightningQubit, LightningQubit2 +import pytest +from conftest import LightningDevice # tested device from pennylane.devices import DefaultQubit -from conftest import LightningDevice # tested device +from pennylane_lightning.lightning_qubit import LightningQubit, LightningQubit2 if LightningDevice != LightningQubit: pytest.skip("Exclusive tests for lightning.qubit. Skipping.", allow_module_level=True) diff --git a/tests/test_apply.py b/tests/test_apply.py index 5af7d4101a..f5337f5082 100644 --- a/tests/test_apply.py +++ b/tests/test_apply.py @@ -30,6 +30,7 @@ from pennylane.wires import Wires +@pytest.mark.skipif(ld._new_API, reason="Old API required") class TestApply: """Tests that operations of certain operations are applied correctly or that the proper errors are raised. @@ -532,6 +533,7 @@ def test_apply_state_vector_lightning_handle(self, qubit_device, tol): class TestExpval: """Tests that expectation values are properly calculated or that the proper errors are raised.""" + @pytest.mark.skipif(ld._new_API, reason="Old API required") @pytest.mark.parametrize( "operation,input,expected_output", [ @@ -584,6 +586,7 @@ def circuit(): class TestVar: """Tests that variances are properly calculated.""" + @pytest.mark.skipif(ld._new_API, reason="Old API required") @pytest.mark.parametrize( "operation,input,expected_output", [ @@ -670,6 +673,7 @@ def test_sample_dimensions(self, qubit_device): s3 = dev.sample(qml.PauliX(0) @ qml.PauliZ(1)) assert np.array_equal(s3.shape, (17,)) + @pytest.mark.skipif(ld._new_API, reason="Old API required") def test_sample_values(self, qubit_device, tol): """Tests if the samples returned by sample have the correct values diff --git a/tests/test_decomposition.py b/tests/test_decomposition.py index 29a7874871..df9de740e2 100644 --- a/tests/test_decomposition.py +++ b/tests/test_decomposition.py @@ -23,6 +23,7 @@ pytest.skip("No binary module found. Skipping.", allow_module_level=True) +@pytest.mark.skipif(ld._new_API, reason="Old API required") class TestDenseMatrixDecompositionThreshold: """Tests, for QFT and Grover operators, the automatic transition from full matrix to decomposition on calculations.""" diff --git a/tests/test_device.py b/tests/test_device.py index 98691b6fd1..11945e89aa 100644 --- a/tests/test_device.py +++ b/tests/test_device.py @@ -37,7 +37,7 @@ def test_create_device_with_dtype(C): not hasattr(np, "complex256"), reason="Numpy only defines complex256 in Linux-like system" ) def test_create_device_with_unsupported_dtype(): - with pytest.raises(TypeError, match="Unsupported complex Type:"): + with pytest.raises(TypeError, match="Unsupported complex type:"): dev = qml.device(device_name, wires=1, c_dtype=np.complex256) diff --git a/tests/test_expval.py b/tests/test_expval.py index 95a985c362..f2bc5b0232 100644 --- a/tests/test_expval.py +++ b/tests/test_expval.py @@ -19,9 +19,10 @@ import numpy as np import pennylane as qml import pytest -from conftest import PHI, THETA, VARPHI, device_name +from conftest import PHI, THETA, VARPHI, LightningDevice, device_name +@pytest.mark.skipif(LightningDevice._new_API, reason="Old API required") @pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI))) class TestExpval: """Test expectation values""" @@ -248,6 +249,7 @@ def circuit(x, y): assert qml.math.allclose(g, expected) +@pytest.mark.skipif(LightningDevice._new_API, reason="Old API required") @pytest.mark.parametrize("theta,phi,varphi", list(zip(THETA, PHI, VARPHI))) class TestTensorExpval: """Test tensor expectation values""" diff --git a/tests/test_gates.py b/tests/test_gates.py index 847c3a845a..231993989d 100644 --- a/tests/test_gates.py +++ b/tests/test_gates.py @@ -244,6 +244,7 @@ def output(input): assert np.allclose(unitary, random_unitary_inv) +@pytest.mark.skipif(LightningDevice._new_API, reason="Old API required") @pytest.mark.skipif(not LightningDevice._CPP_BINARY_AVAILABLE, reason="Lightning binary required") @pytest.mark.parametrize( "obs,has_rotation", @@ -320,7 +321,7 @@ def circuit(): @pytest.mark.skipif( - device_name != "lightning.qubit", + device_name not in ("lightning.qubit", "lightning.qubit2"), reason="N-controlled operations only implemented in lightning.qubit.", ) @pytest.mark.parametrize("control_value", [False, True]) @@ -363,7 +364,7 @@ def circuit(): @pytest.mark.skipif( - device_name != "lightning.qubit", + device_name not in ("lightning.qubit", "lightning.qubit2"), reason="N-controlled operations only implemented in lightning.qubit.", ) @pytest.mark.parametrize( @@ -440,7 +441,7 @@ def circuit(): @pytest.mark.skipif( - device_name != "lightning.qubit", + device_name not in ("lightning.qubit", "lightning.qubit2"), reason="N-controlled operations only implemented in lightning.qubit.", ) def test_controlled_qubit_unitary_from_op(tol): @@ -461,7 +462,7 @@ def circuit(x): @pytest.mark.skipif( - device_name != "lightning.qubit", + device_name not in ("lightning.qubit", "lightning.qubit2"), reason="N-controlled operations only implemented in lightning.qubit.", ) @pytest.mark.parametrize("control_wires", range(4)) diff --git a/tests/test_measurements.py b/tests/test_measurements.py index 62960f92fd..ea57a50f8c 100644 --- a/tests/test_measurements.py +++ b/tests/test_measurements.py @@ -27,6 +27,7 @@ pytest.skip("No binary module found. Skipping.", allow_module_level=True) +@pytest.mark.skipif(ld._new_API, reason="Old API required") def test_measurements(): dev = qml.device(device_name, wires=2) m = dev.measurements @@ -54,6 +55,7 @@ class TestProbs: def dev(self, request): return qml.device(device_name, wires=2, c_dtype=request.param) + @pytest.mark.skipif(ld._new_API, reason="Old API required") def test_probs_dtype64(self, dev): """Test if probs changes the state dtype""" _state = dev._asarray( @@ -119,6 +121,7 @@ def circuit(): assert np.allclose(circuit(), cases[1], atol=tol, rtol=0) + @pytest.mark.skipif(ld._new_API, reason="Old API required") @pytest.mark.parametrize( "cases", [ @@ -198,6 +201,7 @@ def circuit(): assert np.allclose(circuit(), cases[1], atol=tol, rtol=0) + @pytest.mark.skipif(ld._new_API, reason="Old API required") @pytest.mark.parametrize( "cases", [ @@ -235,6 +239,7 @@ class TestExpval: def dev(self, request): return qml.device(device_name, wires=2, c_dtype=request.param) + @pytest.mark.skipif(ld._new_API, reason="Old API required") def test_expval_dtype64(self, dev): """Test if expval changes the state dtype""" _state = np.array([1, 0, 0, 0]).astype(dev.C_DTYPE) @@ -349,7 +354,7 @@ def circuit(): qml.RX(0.52, wires=0) return qml.expval(qml.RX(0.742, wires=[0])) - with pytest.raises(qml._device.DeviceError, match="Observable RX not supported"): + with pytest.raises(qml._device.DeviceError, match="Observable RX.*not supported"): circuit() def test_observable_return_type_is_expectation(self, dev): @@ -371,6 +376,7 @@ class TestVar: def dev(self, request): return qml.device(device_name, wires=2, c_dtype=request.param) + @pytest.mark.skipif(ld._new_API, reason="Old API required") def test_var_dtype64(self, dev): """Test if var changes the state dtype""" _state = np.array([1, 0, 0, 0]).astype(np.complex64) @@ -449,7 +455,7 @@ def circuit(): qml.RX(0.52, wires=0) return qml.var(qml.RX(0.742, wires=[0])) - with pytest.raises(qml._device.DeviceError, match="Observable RX not supported"): + with pytest.raises(qml._device.DeviceError, match="Observable RX.*not supported"): circuit() def test_observable_return_type_is_variance(self, dev): @@ -478,13 +484,14 @@ def circuit(): qml.RX(0.52, wires=0) return qml.var(qml.RX(0.742, wires=[0])) - with pytest.raises(qml._device.DeviceError, match="Observable RX not supported"): + with pytest.raises(qml._device.DeviceError, match="Observable RX.*not supported"): circuit() class TestWiresInExpval: """Test different Wires settings in Lightning's expval.""" + @pytest.mark.skipif(ld._new_API, reason="Old API required") @pytest.mark.parametrize( "wires1, wires2", [ @@ -529,6 +536,7 @@ def circuit2(): assert np.allclose(circuit1(), circuit2(), atol=tol) + @pytest.mark.skipif(ld._new_API, reason="Old API required") @pytest.mark.parametrize( "wires1, wires2", [ @@ -585,6 +593,7 @@ def circuit2(): class TestSample: """Tests that samples are properly calculated.""" + @pytest.mark.skipif(ld._new_API, reason="Old API required") @pytest.mark.parametrize( "shots, wires", [ @@ -607,6 +616,7 @@ def test_sample_dimensions(self, qubit_device, shots, wires): s1 = dev.sample(qml.PauliZ(wires=[0])) assert np.array_equal(s1.shape, (dev.shots,)) + @pytest.mark.skipif(ld._new_API, reason="Old API required") def test_sample_values(self, qubit_device, tol): """Tests if the samples returned by sample have the correct values @@ -628,6 +638,7 @@ def test_sample_values(self, qubit_device, tol): class TestWiresInVar: """Test different Wires settings in Lightning's var.""" + @pytest.mark.skipif(ld._new_API, reason="Old API required") @pytest.mark.parametrize( "wires1, wires2", [ diff --git a/tests/test_var.py b/tests/test_var.py index bf0779da6f..fc73a4d748 100644 --- a/tests/test_var.py +++ b/tests/test_var.py @@ -17,7 +17,7 @@ import numpy as np import pennylane as qml import pytest -from conftest import PHI, THETA, VARPHI +from conftest import PHI, THETA, VARPHI, LightningDevice np.random.seed(42) @@ -26,6 +26,7 @@ class TestVar: """Tests for the variance""" + @pytest.mark.skipif(LightningDevice._new_API, reason="Old API required") def test_var(self, theta, phi, qubit_device, tol): """Tests for variance calculation""" dev = qubit_device(wires=3) @@ -71,6 +72,7 @@ def circuit(): assert np.allclose(circ(), circ_def(), tol) +@pytest.mark.skipif(LightningDevice._new_API, reason="Old API required") @pytest.mark.parametrize("theta, phi, varphi", list(zip(THETA, PHI, VARPHI))) class TestTensorVar: """Tests for variance of tensor observables""" From e57642c34aa69cad3f3ecd1de373ff0e209d2726 Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Tue, 5 Mar 2024 19:50:12 +0000 Subject: [PATCH 110/428] Fix few more tests. --- tests/test_apply.py | 33 +++++++++++++++++++++++---------- tests/test_expval.py | 8 ++++++++ 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/tests/test_apply.py b/tests/test_apply.py index f5337f5082..b2d6cb441f 100644 --- a/tests/test_apply.py +++ b/tests/test_apply.py @@ -640,6 +640,7 @@ def circuit(): class TestSample: """Tests that samples are properly calculated.""" + @pytest.mark.skipif(ld._new_API, reason="Old API required") def test_sample_dimensions(self, qubit_device): """Tests if the samples returned by the sample function have the correct dimensions @@ -808,7 +809,8 @@ def test_supported_gate_single_wire_no_parameters( dev = qubit_device(wires=1) op = getattr(qml.ops, name) - assert dev.supports_operation(name) + if hasattr(dev, "supports_operation"): + assert dev.supports_operation(name) @qml.qnode(dev) def circuit(): @@ -834,7 +836,8 @@ def test_supported_gate_two_wires_no_parameters( dev = qubit_device(wires=2) op = getattr(qml.ops, name) - assert dev.supports_operation(name) + if hasattr(dev, "supports_operation"): + assert dev.supports_operation(name) @qml.qnode(dev) def circuit(): @@ -857,7 +860,8 @@ def test_supported_gate_three_wires_no_parameters( dev = qubit_device(wires=3) op = getattr(qml.ops, name) - assert dev.supports_operation(name) + if hasattr(dev, "supports_operation"): + assert dev.supports_operation(name) @qml.qnode(dev) def circuit(): @@ -884,7 +888,8 @@ def test_supported_state_preparation(self, qubit_device, tol, name, par, expecte dev = qubit_device(wires=2) op = getattr(qml.ops, name) - assert dev.supports_operation(name) + if hasattr(dev, "supports_operation"): + assert dev.supports_operation(name) @qml.qnode(dev) def circuit(): @@ -1002,7 +1007,8 @@ def test_supported_gate_single_wire_with_parameters( dev = qubit_device(wires=1) op = getattr(qml.ops, name) - assert dev.supports_operation(name) + if hasattr(dev, "supports_operation"): + assert dev.supports_operation(name) @qml.qnode(dev) def circuit(): @@ -1044,7 +1050,8 @@ def test_supported_gate_two_wires_with_parameters( dev = qubit_device(wires=2) op = getattr(qml.ops, name) - assert dev.supports_operation(name) + if hasattr(dev, "supports_operation"): + assert dev.supports_operation(name) @qml.qnode(dev) def circuit(): @@ -1079,7 +1086,8 @@ def test_supported_observable_single_wire_no_parameters( dev = qubit_device(wires=1) obs = getattr(qml.ops, name) - assert dev.supports_observable(name) + if hasattr(dev, "supports_observable"): + assert dev.supports_observable(name) @qml.qnode(dev) def circuit(): @@ -1104,7 +1112,8 @@ def test_supported_observable_single_wire_with_parameters( dev = qubit_device(wires=1) obs = getattr(qml.ops, name) - assert dev.supports_observable(name) + if hasattr(dev, "supports_observable"): + assert dev.supports_observable(name) @qml.qnode(dev) def circuit(): @@ -1192,10 +1201,13 @@ def circuit(): qml.QuantumPhaseEstimation(qml.matrix(qml.Hadamard)(wires=0), [0], [1]) return qml.probs(wires=[0, 1]) - circuit() + probs = circuit() res_sv = dev.state - res_probs = dev.probability([0, 1]) + if ld._new_API: + res_probs = probs + else: + res_probs = dev.probability([0, 1]) expected_sv = np.array( [ @@ -1214,6 +1226,7 @@ def circuit(): class TestApplyLightningMethod: """Unit tests for the apply_lightning method.""" + @pytest.mark.skipif(ld._new_API, reason="Old API required") def test_apply_identity_skipped(self, mocker, tol): """Test identity operation does not perform additional computations.""" dev = qml.device(device_name, wires=1) diff --git a/tests/test_expval.py b/tests/test_expval.py index f2bc5b0232..d1b27f9658 100644 --- a/tests/test_expval.py +++ b/tests/test_expval.py @@ -170,6 +170,8 @@ def test_sprod(self, diff_method, qubit_device): """Test the `SProd` class with lightning qubit.""" dev = qubit_device(wires=2) + if diff_method == "adjoint" and dev.short_name == "lightning.qubit2": + return @qml.qnode(dev, diff_method=diff_method) def circuit(x): @@ -188,6 +190,8 @@ def test_prod(self, diff_method, qubit_device): """Test the `Prod` class with lightning qubit.""" dev = qubit_device(wires=2) + if diff_method == "adjoint" and dev.short_name == "lightning.qubit2": + return @qml.qnode(dev, diff_method=diff_method) def circuit(x): @@ -208,6 +212,8 @@ def test_sum(self, diff_method, qubit_device): """Test the `Sum` class with lightning qubit.""" dev = qubit_device(wires=2) + if diff_method == "adjoint" and dev.short_name == "lightning.qubit2": + return @qml.qnode(dev, diff_method=diff_method) def circuit(x, y): @@ -230,6 +236,8 @@ def test_integration(self, diff_method, qubit_device): obs = qml.sum(qml.s_prod(2.3, qml.PauliZ(0)), -0.5 * qml.prod(qml.PauliY(0), qml.PauliZ(1))) dev = qubit_device(wires=2) + if diff_method == "adjoint" and dev.short_name == "lightning.qubit2": + return @qml.qnode(dev, diff_method=diff_method) def circuit(x, y): From 1c8f20c85f60a384f59ef808e672d5c6bcff5eb3 Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Tue, 5 Mar 2024 20:01:45 +0000 Subject: [PATCH 111/428] Skip shots, adjoint, vjp with new API. --- tests/test_adjoint_jacobian.py | 3 +++ tests/test_apply.py | 10 ++++++++++ tests/test_vjp.py | 3 +++ 3 files changed, 16 insertions(+) diff --git a/tests/test_adjoint_jacobian.py b/tests/test_adjoint_jacobian.py index ad9db67239..df73ec46ca 100644 --- a/tests/test_adjoint_jacobian.py +++ b/tests/test_adjoint_jacobian.py @@ -26,6 +26,9 @@ from pennylane import qchem, qnode from scipy.stats import unitary_group +if ld._new_API: + pytest.skip("Old API required", allow_module_level=True) + I, X, Y, Z = ( np.eye(2), qml.PauliX.compute_matrix(), diff --git a/tests/test_apply.py b/tests/test_apply.py index b2d6cb441f..8aae0e0553 100644 --- a/tests/test_apply.py +++ b/tests/test_apply.py @@ -568,6 +568,7 @@ def test_expval_single_wire_no_parameters( assert np.isclose(res, expected_output, atol=tol, rtol=0) + @pytest.mark.xfail(ld._new_API, reason="Old API required") def test_expval_estimate(self): """Test that the expectation value is not analytically calculated""" dev = qml.device(device_name, wires=1, shots=3) @@ -621,6 +622,7 @@ def test_var_single_wire_no_parameters( assert np.isclose(res, expected_output, atol=tol, rtol=0) + @pytest.mark.xfail(ld._new_API, reason="Old API required") def test_var_estimate(self): """Test that the variance is not analytically calculated""" @@ -702,6 +704,7 @@ class TestLightningDeviceIntegration: """Integration tests for lightning device. This test ensures it integrates properly with the PennyLane interface, in particular QNode.""" + @pytest.mark.xfail(ld._new_API, reason="Old API required") def test_load_default_qubit_device(self): """Test that the default plugin loads correctly""" @@ -710,6 +713,7 @@ def test_load_default_qubit_device(self): assert dev.shots is None assert dev.short_name == device_name + @pytest.mark.xfail(ld._new_API, reason="Old API required") @pytest.mark.skipif(not ld._CPP_BINARY_AVAILABLE, reason="Lightning binary required") def test_no_backprop(self): """Test that lightning device does not support the backprop @@ -724,6 +728,7 @@ def circuit(): with pytest.raises(qml.QuantumFunctionError): qml.QNode(circuit, dev, diff_method="backprop") + @pytest.mark.xfail(ld._new_API, reason="Old API required") @pytest.mark.skipif(not ld._CPP_BINARY_AVAILABLE, reason="Lightning binary required") def test_best_gets_lightning(self): """Test that the best differentiation method returns lightning @@ -772,6 +777,7 @@ def circuit(x): assert np.isclose(circuit(p), 1, atol=tol, rtol=0) + @pytest.mark.xfail(ld._new_API, reason="Old API required") def test_nonzero_shots(self, tol_stochastic): """Test that the default qubit plugin provides correct result for high shot number""" @@ -1122,6 +1128,7 @@ def circuit(): assert np.isclose(circuit(), expected_output, atol=tol, rtol=0) + @pytest.mark.xfail(ld._new_API, reason="Old API required") def test_multi_samples_return_correlated_results(self, qubit_device): """Tests if the samples returned by the sample function have the correct dimensions @@ -1140,6 +1147,7 @@ def circuit(): assert np.array_equal(outcomes[0], outcomes[1]) @pytest.mark.parametrize("num_wires", [3, 4, 5, 6, 7, 8]) + @pytest.mark.xfail(ld._new_API, reason="Old API required") def test_multi_samples_return_correlated_results_more_wires_than_size_of_observable( self, num_wires ): @@ -1159,6 +1167,7 @@ def circuit(): assert np.array_equal(outcomes[0], outcomes[1]) + @pytest.mark.xfail(ld._new_API, reason="Old API required") def test_snapshot_is_ignored_without_shot(self): """Tests if the Snapshot operator is ignored correctly""" dev = qml.device(device_name, wires=4) @@ -1175,6 +1184,7 @@ def circuit(): assert np.allclose(outcomes, [0.0]) + @pytest.mark.xfail(ld._new_API, reason="Old API required") def test_snapshot_is_ignored_with_shots(self): """Tests if the Snapshot operator is ignored correctly""" dev = qml.device(device_name, wires=4, shots=1000) diff --git a/tests/test_vjp.py b/tests/test_vjp.py index d53b9b4135..70bd091bf9 100644 --- a/tests/test_vjp.py +++ b/tests/test_vjp.py @@ -25,6 +25,9 @@ if not ld._CPP_BINARY_AVAILABLE: pytest.skip("No binary module found. Skipping.", allow_module_level=True) +if ld._new_API: + pytest.skip("Old API required", allow_module_level=True) + class TestVectorJacobianProduct: """Tests for the `vjp` function""" From 5e80afb64b4efba99141cd798ff36cb52403a90d Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Tue, 5 Mar 2024 16:52:58 -0500 Subject: [PATCH 112/428] remove diagonalization gate application from state vector --- pennylane_lightning/lightning_qubit/_measurements.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/_measurements.py b/pennylane_lightning/lightning_qubit/_measurements.py index efe38a1758..c5d43ebfee 100644 --- a/pennylane_lightning/lightning_qubit/_measurements.py +++ b/pennylane_lightning/lightning_qubit/_measurements.py @@ -27,6 +27,7 @@ from typing import Callable, List import numpy as np +import pennylane as qml from pennylane.measurements import ExpectationMP, MeasurementProcess, StateMeasurement from pennylane.tape import QuantumScript from pennylane.typing import Result, TensorLike @@ -76,7 +77,7 @@ def _measurement_dtype(self): def state_diagonalizing_gates(self, measurementprocess: StateMeasurement) -> TensorLike: """Apply a measurement to state when the measurement process has an observable with diagonalizing gates. - This method will is bypassing the measurement process to default.qubit implementation. + This method is bypassing the measurement process to default.qubit implementation. Args: measurementprocess (StateMeasurement): measurement to apply to the state @@ -84,11 +85,17 @@ def state_diagonalizing_gates(self, measurementprocess: StateMeasurement) -> Ten Returns: TensorLike: the result of the measurement """ + diagonalizing_gates = measurementprocess.diagonalizing_gates() self._qubit_state.apply_operations(measurementprocess.diagonalizing_gates()) state_array = self._qubit_state.state wires = Wires(range(self._qubit_state.num_wires)) - return measurementprocess.process_state(state_array, wires) + + result = measurementprocess.process_state(state_array, wires) + + self._qubit_state.apply_operations([qml.adjoint(g) for g in reversed(diagonalizing_gates)]) + + return result # pylint: disable=protected-access def expval(self, measurementprocess: MeasurementProcess): From 96618ca817bceab50dc8439ec4445ba69dd6c771 Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Tue, 5 Mar 2024 22:26:01 +0000 Subject: [PATCH 113/428] pytest.skip tests --- .../lightning_gpu/lightning_gpu.py | 14 +++++------ .../lightning_kokkos/lightning_kokkos.py | 12 +++++----- .../test_measurements_class.py | 23 +++++++++++++++---- 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/pennylane_lightning/lightning_gpu/lightning_gpu.py b/pennylane_lightning/lightning_gpu/lightning_gpu.py index 1ff0642620..be36ff548d 100644 --- a/pennylane_lightning/lightning_gpu/lightning_gpu.py +++ b/pennylane_lightning/lightning_gpu/lightning_gpu.py @@ -91,13 +91,6 @@ from pennylane.ops.op_math import Adjoint from pennylane.wires import Wires - # pylint: disable=import-error, no-name-in-module, ungrouped-imports - from pennylane_lightning.core._serialize import ( - QuantumScriptSerializer, - global_phase_diagonal, - ) - from pennylane_lightning.core._version import __version__ - # pylint: disable=no-name-in-module, ungrouped-imports from pennylane_lightning.lightning_gpu_ops.algorithms import ( AdjointJacobianC64, @@ -106,6 +99,13 @@ create_ops_listC128, ) + # pylint: disable=import-error, no-name-in-module, ungrouped-imports + from pennylane_lightning.core._serialize import ( + QuantumScriptSerializer, + global_phase_diagonal, + ) + from pennylane_lightning.core._version import __version__ + if MPI_SUPPORT: from pennylane_lightning.lightning_gpu_ops.algorithmsMPI import ( AdjointJacobianMPIC64, diff --git a/pennylane_lightning/lightning_kokkos/lightning_kokkos.py b/pennylane_lightning/lightning_kokkos/lightning_kokkos.py index 9844d86ec9..90c53cefe7 100644 --- a/pennylane_lightning/lightning_kokkos/lightning_kokkos.py +++ b/pennylane_lightning/lightning_kokkos/lightning_kokkos.py @@ -63,6 +63,12 @@ from pennylane.operation import Tensor from pennylane.ops.op_math import Adjoint from pennylane.wires import Wires + from pennylane_lightning.lightning_kokkos_ops.algorithms import ( + AdjointJacobianC64, + AdjointJacobianC128, + create_ops_listC64, + create_ops_listC128, + ) # pylint: disable=import-error, no-name-in-module, ungrouped-imports from pennylane_lightning.core._serialize import ( @@ -70,12 +76,6 @@ global_phase_diagonal, ) from pennylane_lightning.core._version import __version__ - from pennylane_lightning.lightning_kokkos_ops.algorithms import ( - AdjointJacobianC64, - AdjointJacobianC128, - create_ops_listC64, - create_ops_listC128, - ) def _kokkos_dtype(dtype): if dtype not in [np.complex128, np.complex64]: # pragma: no cover diff --git a/tests/lightning_qubit/test_measurements_class.py b/tests/lightning_qubit/test_measurements_class.py index faa8dedef0..3d902d2ef5 100644 --- a/tests/lightning_qubit/test_measurements_class.py +++ b/tests/lightning_qubit/test_measurements_class.py @@ -399,7 +399,8 @@ def calculate_reference(tape, lightning_sv): use_default = True new_meas = [] for m in tape.measurements: - # not supported by DefaultQubit + # NotImplementedError in DefaultQubit + # We therefore validate against `qml.Hermitian` if isinstance(m, VarianceMP) and isinstance( m.obs, (qml.Hamiltonian, qml.SparseHamiltonian) ): @@ -445,9 +446,15 @@ def test_single_return_value(self, measurement, observable, lightning_sv, tol): observable, (qml.ops.Sum, qml.ops.SProd, qml.ops.Prod, qml.Hamiltonian, qml.SparseHamiltonian), ): - return + pytest.skip( + f"Observable of type {type(observable).__name__} is not supported for rotating probabilities." + ) + if measurement is not qml.probs and isinstance(observable, list): - return + pytest.skip( + f"Measurement of type {type(measurement).__name__} does not have a keyword argument 'wires'." + ) + n_qubits = 4 n_layers = 1 np.random.seed(0) @@ -510,12 +517,18 @@ def test_double_return_value(self, measurement, obs0_, obs1_, lightning_sv, tol) obs0_, (qml.ops.Sum, qml.ops.SProd, qml.ops.Prod, qml.Hamiltonian, qml.SparseHamiltonian), ): - return + pytest.skip( + f"Observable of type {type(obs0_).__name__} is not supported for rotating probabilities." + ) + if measurement is qml.probs and isinstance( obs1_, (qml.ops.Sum, qml.ops.SProd, qml.ops.Prod, qml.Hamiltonian, qml.SparseHamiltonian), ): - return + pytest.skip( + f"Observable of type {type(obs1_).__name__} is not supported for rotating probabilities." + ) + n_qubits = 4 n_layers = 1 np.random.seed(0) From 24976a944e32f31cdf4278604e99a7e70a968003 Mon Sep 17 00:00:00 2001 From: Dev version update bot Date: Wed, 6 Mar 2024 13:03:17 +0000 Subject: [PATCH 114/428] Auto update version --- pennylane_lightning/core/_version.py | 2 +- tests/lightning_qubit/test_measurements_class.py | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index 5ef6d062a5..b8174c108a 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.36.0-dev2" +__version__ = "0.36.0-dev3" diff --git a/tests/lightning_qubit/test_measurements_class.py b/tests/lightning_qubit/test_measurements_class.py index f1fd78c32a..ebd394b604 100644 --- a/tests/lightning_qubit/test_measurements_class.py +++ b/tests/lightning_qubit/test_measurements_class.py @@ -92,8 +92,6 @@ def test_only_support_state_measurements(self, lightning_sv): @pytest.mark.parametrize( "mp", ( - qml.probs(wires=0), - qml.var(qml.Z(0)), qml.vn_entropy(wires=0), CustomStateMeasurement(), qml.expval(qml.Identity(0)), From fda2b3ef58c695efdb899b930c38a65aef43364e Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Wed, 6 Mar 2024 13:23:28 +0000 Subject: [PATCH 115/428] Fix format --- pennylane_lightning/lightning_kokkos/lightning_kokkos.py | 2 ++ pennylane_lightning/lightning_qubit/_measurements.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/pennylane_lightning/lightning_kokkos/lightning_kokkos.py b/pennylane_lightning/lightning_kokkos/lightning_kokkos.py index 90c53cefe7..9953e2651c 100644 --- a/pennylane_lightning/lightning_kokkos/lightning_kokkos.py +++ b/pennylane_lightning/lightning_kokkos/lightning_kokkos.py @@ -63,6 +63,8 @@ from pennylane.operation import Tensor from pennylane.ops.op_math import Adjoint from pennylane.wires import Wires + + # pylint: disable=import-error, no-name-in-module, ungrouped-imports from pennylane_lightning.lightning_kokkos_ops.algorithms import ( AdjointJacobianC64, AdjointJacobianC128, diff --git a/pennylane_lightning/lightning_qubit/_measurements.py b/pennylane_lightning/lightning_qubit/_measurements.py index 7b8e9af041..28e227c5d9 100644 --- a/pennylane_lightning/lightning_qubit/_measurements.py +++ b/pennylane_lightning/lightning_qubit/_measurements.py @@ -138,7 +138,7 @@ def expval(self, measurementprocess: MeasurementProcess): measurementprocess.obs.name, measurementprocess.obs.wires ) -# pylint: disable=protected-access + # pylint: disable=protected-access def probs(self, measurementprocess: MeasurementProcess): """Probabilities of the supplied observable or wires contained in the MeasurementProcess. From 4adccf258f80f998c022d7c8c9faccc91441039b Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Wed, 6 Mar 2024 13:30:03 +0000 Subject: [PATCH 116/428] Fix no-bin interface. --- pennylane_lightning/core/lightning_base.py | 1 + pennylane_lightning/lightning_qubit/lightning_qubit.py | 8 ++++---- pennylane_lightning/lightning_qubit/lightning_qubit2.py | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/pennylane_lightning/core/lightning_base.py b/pennylane_lightning/core/lightning_base.py index 61876ac766..88f2d78030 100644 --- a/pennylane_lightning/core/lightning_base.py +++ b/pennylane_lightning/core/lightning_base.py @@ -397,6 +397,7 @@ class LightningBaseFallBack(DefaultQubitLegacy): # pragma: no cover version = __version__ author = "Xanadu Inc." _CPP_BINARY_AVAILABLE = False + _new_API = False def __init__(self, wires, *, c_dtype=np.complex128, **kwargs): if c_dtype is np.complex64: diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit.py b/pennylane_lightning/lightning_qubit/lightning_qubit.py index f220653311..3571cd05f4 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit.py @@ -62,10 +62,6 @@ from pennylane.measurements import Expectation, MeasurementProcess, State from pennylane.operation import Tensor from pennylane.wires import Wires - - # pylint: disable=import-error, no-name-in-module, ungrouped-imports - from pennylane_lightning.core._serialize import QuantumScriptSerializer - from pennylane_lightning.core._version import __version__ from pennylane_lightning.lightning_qubit_ops.algorithms import ( AdjointJacobianC64, AdjointJacobianC128, @@ -75,6 +71,10 @@ create_ops_listC128, ) + # pylint: disable=import-error, no-name-in-module, ungrouped-imports + from pennylane_lightning.core._serialize import QuantumScriptSerializer + from pennylane_lightning.core._version import __version__ + def _state_dtype(dtype): if dtype not in [np.complex128, np.complex64]: # pragma: no cover raise ValueError(f"Data type is not supported for state-vector computation: {dtype}") diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index 47dc9f06be..6bdc24fe68 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -206,7 +206,7 @@ class LightningQubit2(Device): short_name = "lightning.qubit2" operations = _operations observables = _observables - _backend_info = backend_info + _backend_info = backend_info if LQ_CPP_BINARY_AVAILABLE else None config = Path(__file__).parent / "lightning_qubit.toml" def __init__( # pylint: disable=too-many-arguments From 09f3bfa0fb1c506f75704161d252507167d84037 Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Tue, 5 Mar 2024 22:49:19 +0000 Subject: [PATCH 117/428] WIP --- .../lightning_qubit/_measurements.py | 125 +++++++++++++++++- 1 file changed, 120 insertions(+), 5 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/_measurements.py b/pennylane_lightning/lightning_qubit/_measurements.py index 28e227c5d9..014318971a 100644 --- a/pennylane_lightning/lightning_qubit/_measurements.py +++ b/pennylane_lightning/lightning_qubit/_measurements.py @@ -29,9 +29,12 @@ import numpy as np import pennylane as qml from pennylane.measurements import ( + CountsMP, ExpectationMP, MeasurementProcess, ProbabilityMP, + SampleMeasurement, + Shots, StateMeasurement, VarianceMP, ) @@ -253,10 +256,122 @@ def measure_final_state(self, circuit: QuantumScript) -> Result: Tuple[TensorLike]: The measurement results """ - if circuit.shots: - raise NotImplementedError - # analytic case + if not circuit.shots: + # analytic case + if len(circuit.measurements) == 1: + return self.measurement(circuit.measurements[0]) + + return tuple(self.measurement(mp) for mp in circuit.measurements) + + # finite-shot case + results = [ + self.measure_with_samples( + mp, + shots=circuit.shots, + ) + for mp in circuit.measurements + ] + if len(circuit.measurements) == 1: - return self.measurement(circuit.measurements[0]) + if circuit.shots.has_partitioned_shots: + return tuple(res[0] for res in results) + + return results[0] + + return results + + def measure_with_samples( + self, + measurementprocess: SampleMeasurement, + shots: Shots, + ) -> TensorLike: + """ + Returns the samples of the measurement process performed on the given state, + by rotating the state into the measurement basis using the diagonalizing gates + given by the measurement process. + + Args: + mp (~.measurements.SampleMeasurement): The sample measurement to perform + state (np.ndarray[complex]): The state vector 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 + seed-like parameter matching that of ``seed`` for ``numpy.random.default_rng``. + If no value is provided, a default RNG will be used. + prng_key (Optional[jax.random.PRNGKey]): An optional ``jax.random.PRNGKey``. This is + the key to the JAX pseudo random number generator. Only for simulation using JAX. - return tuple(self.measurement(mp) for mp in circuit.measurements) + Returns: + TensorLike[Any]: Sample measurement results + """ + diagonalizing_gates = measurementprocess.diagonalizing_gates() + if diagonalizing_gates: + self._qubit_state.apply_operations(diagonalizing_gates) + + total_indices = len(state.shape) - is_state_batched + wires = qml.wires.Wires(range(total_indices)) + + def _process_single_shot(samples): + processed = [] + res = measurementprocess.process_samples(samples, wires) + if not isinstance(measurementprocess, CountsMP): + res = qml.math.squeeze(res) + + processed.append(res) + + return tuple(processed) + + # if there is a shot vector, build a list containing results for each shot entry + if shots.has_partitioned_shots: + processed_samples = [] + for s in shots: + # currently we call sample_state for each shot entry, but it may be + # better to call sample_state just once with total_shots, then use + # the shot_range keyword argument + try: + samples = self._measurement_lightning.generate_samples( + len(measurementprocess.wires), s + ).astype(int, copy=False) + # samples = sample_state( + # state, + # shots=s, + # is_state_batched=is_state_batched, + # wires=wires, + # rng=rng, + # prng_key=prng_key, + # ) + except ValueError as e: + if str(e) != "probabilities contain NaN": + raise e + samples = qml.math.full((s, len(wires)), 0) + + processed_samples.append(_process_single_shot(samples)) + if diagonalizing_gates: + self._qubit_state.apply_operations( + [qml.adjoint(g) for g in reversed(diagonalizing_gates)] + ) + + return tuple(zip(*processed_samples)) + + try: + samples = self._measurement_lightning.generate_samples( + len(measurementprocess.wires), shots.total_shots + ).astype(int, copy=False) + # sample_state( + # state, + # shots=shots.total_shots, + # is_state_batched=is_state_batched, + # wires=wires, + # rng=rng, + # prng_key=prng_key, + # ) + except ValueError as e: + if str(e) != "probabilities contain NaN": + raise e + samples = qml.math.full((shots.total_shots, len(wires)), 0) + + if diagonalizing_gates: + self._qubit_state.apply_operations( + [qml.adjoint(g) for g in reversed(diagonalizing_gates)] + ) + return _process_single_shot(samples) From aa1d1a88f52b333224788881b346d76b56b7699c Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Wed, 6 Mar 2024 14:10:46 +0000 Subject: [PATCH 118/428] Initial shots support + fix test_measurement tests. --- .../lightning_qubit/_measurements.py | 39 ++++------------- .../lightning_qubit/lightning_qubit.py | 8 ++-- .../lightning_qubit/lightning_qubit2.py | 1 - tests/conftest.py | 4 +- tests/test_measurements.py | 43 +++++++++++-------- 5 files changed, 38 insertions(+), 57 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/_measurements.py b/pennylane_lightning/lightning_qubit/_measurements.py index 014318971a..26a076c225 100644 --- a/pennylane_lightning/lightning_qubit/_measurements.py +++ b/pennylane_lightning/lightning_qubit/_measurements.py @@ -264,13 +264,13 @@ def measure_final_state(self, circuit: QuantumScript) -> Result: return tuple(self.measurement(mp) for mp in circuit.measurements) # finite-shot case - results = [ + results = tuple( self.measure_with_samples( mp, shots=circuit.shots, ) for mp in circuit.measurements - ] + ) if len(circuit.measurements) == 1: if circuit.shots.has_partitioned_shots: @@ -308,18 +308,11 @@ def measure_with_samples( if diagonalizing_gates: self._qubit_state.apply_operations(diagonalizing_gates) - total_indices = len(state.shape) - is_state_batched - wires = qml.wires.Wires(range(total_indices)) + wires = measurementprocess.wires def _process_single_shot(samples): - processed = [] res = measurementprocess.process_samples(samples, wires) - if not isinstance(measurementprocess, CountsMP): - res = qml.math.squeeze(res) - - processed.append(res) - - return tuple(processed) + return res if isinstance(measurementprocess, CountsMP) else qml.math.squeeze(res) # if there is a shot vector, build a list containing results for each shot entry if shots.has_partitioned_shots: @@ -329,17 +322,9 @@ def _process_single_shot(samples): # better to call sample_state just once with total_shots, then use # the shot_range keyword argument try: - samples = self._measurement_lightning.generate_samples( - len(measurementprocess.wires), s - ).astype(int, copy=False) - # samples = sample_state( - # state, - # shots=s, - # is_state_batched=is_state_batched, - # wires=wires, - # rng=rng, - # prng_key=prng_key, - # ) + samples = self._measurement_lightning.generate_samples(len(wires), s).astype( + int, copy=False + ) except ValueError as e: if str(e) != "probabilities contain NaN": raise e @@ -355,16 +340,8 @@ def _process_single_shot(samples): try: samples = self._measurement_lightning.generate_samples( - len(measurementprocess.wires), shots.total_shots + len(wires), shots.total_shots ).astype(int, copy=False) - # sample_state( - # state, - # shots=shots.total_shots, - # is_state_batched=is_state_batched, - # wires=wires, - # rng=rng, - # prng_key=prng_key, - # ) except ValueError as e: if str(e) != "probabilities contain NaN": raise e diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit.py b/pennylane_lightning/lightning_qubit/lightning_qubit.py index 3571cd05f4..f220653311 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit.py @@ -62,6 +62,10 @@ from pennylane.measurements import Expectation, MeasurementProcess, State from pennylane.operation import Tensor from pennylane.wires import Wires + + # pylint: disable=import-error, no-name-in-module, ungrouped-imports + from pennylane_lightning.core._serialize import QuantumScriptSerializer + from pennylane_lightning.core._version import __version__ from pennylane_lightning.lightning_qubit_ops.algorithms import ( AdjointJacobianC64, AdjointJacobianC128, @@ -71,10 +75,6 @@ create_ops_listC128, ) - # pylint: disable=import-error, no-name-in-module, ungrouped-imports - from pennylane_lightning.core._serialize import QuantumScriptSerializer - from pennylane_lightning.core._version import __version__ - def _state_dtype(dtype): if dtype not in [np.complex128, np.complex64]: # pragma: no cover raise ValueError(f"Data type is not supported for state-vector computation: {dtype}") diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index 6bdc24fe68..999b64c442 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -319,7 +319,6 @@ def supports_derivatives( def preprocess(self, execution_config: ExecutionConfig = DefaultExecutionConfig): program = TransformProgram() program.add_transform(validate_measurements, name=self.name) - program.add_transform(no_sampling) program.add_transform(validate_observables, accepted_observables, name=self.name) program.add_transform(validate_device_wires, self.wires, name=self.name) program.add_transform(qml.defer_measurements, device=self) diff --git a/tests/conftest.py b/tests/conftest.py index cbc0d29dde..d3cef75d53 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -149,7 +149,7 @@ def get_device(): params=[np.complex64, np.complex128], ) def qubit_device(request): - def _device(wires): - return qml.device(device_name, wires=wires, c_dtype=request.param) + def _device(wires, shots=None): + return qml.device(device_name, wires=wires, shots=shots, c_dtype=request.param) return _device diff --git a/tests/test_measurements.py b/tests/test_measurements.py index ea57a50f8c..7b7b36525e 100644 --- a/tests/test_measurements.py +++ b/tests/test_measurements.py @@ -593,7 +593,6 @@ def circuit2(): class TestSample: """Tests that samples are properly calculated.""" - @pytest.mark.skipif(ld._new_API, reason="Old API required") @pytest.mark.parametrize( "shots, wires", [ @@ -606,29 +605,35 @@ def test_sample_dimensions(self, qubit_device, shots, wires): """Tests if the samples returned by the sample function have the correct dimensions """ - dev = qubit_device(wires=2) - - dev.apply([qml.RX(1.5708, wires=[0]), qml.RX(1.5708, wires=[1])]) - - dev.shots = shots - dev._wires_measured = wires - dev._samples = dev.generate_samples() - s1 = dev.sample(qml.PauliZ(wires=[0])) - assert np.array_equal(s1.shape, (dev.shots,)) + dev = qubit_device(wires=2, shots=shots) + ops = [qml.RX(1.5708, wires=[0]), qml.RX(1.5708, wires=[1])] + obs = qml.PauliZ(wires=[0]) + if ld._new_API: + tape = qml.tape.QuantumScript(ops, [qml.sample(op=obs)], shots=shots) + s1 = dev.execute(tape) + else: + dev.apply(ops) + dev._wires_measured = wires + dev._samples = dev.generate_samples() + s1 = dev.sample(obs) + assert np.array_equal(s1.shape, (shots,)) - @pytest.mark.skipif(ld._new_API, reason="Old API required") def test_sample_values(self, qubit_device, tol): """Tests if the samples returned by sample have the correct values """ - dev = qubit_device(wires=2) - - dev.shots = 1000 - dev.apply([qml.RX(1.5708, wires=[0])]) - dev._wires_measured = {0} - dev._samples = dev.generate_samples() - - s1 = dev.sample(qml.PauliZ(0)) + shots = 1000 + dev = qubit_device(wires=2, shots=shots) + ops = [qml.RX(1.5708, wires=[0])] + obs = qml.PauliZ(0) + if ld._new_API: + tape = qml.tape.QuantumScript(ops, [qml.sample(op=obs)], shots=shots) + s1 = dev.execute(tape) + else: + dev.apply(ops) + dev._wires_measured = {0} + dev._samples = dev.generate_samples() + s1 = dev.sample(qml.PauliZ(0)) # s1 should only contain 1 and -1, which is guaranteed if # they square to 1 From 095fc5c829ca93acfec39a3b36de537daaac535a Mon Sep 17 00:00:00 2001 From: albi3ro Date: Fri, 16 Feb 2024 13:46:56 -0500 Subject: [PATCH 119/428] update --- .../lightning_qubit/lightning_qubit2.py | 369 ++++++++++++++++++ 1 file changed, 369 insertions(+) create mode 100644 pennylane_lightning/lightning_qubit/lightning_qubit2.py diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py new file mode 100644 index 0000000000..cf32b7f178 --- /dev/null +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -0,0 +1,369 @@ +# Copyright 2018-2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This module contains the LightningQubit2 class that inherits from the new device interface. + +""" +from typing import Union, Sequence, Optional +from dataclasses import replace +import numpy as np + + +try: + # pylint: disable=import-error, no-name-in-module + from pennylane_lightning.lightning_qubit_ops import ( + StateVectorC64, + StateVectorC128, + ) + + LQ_CPP_BINARY_AVAILABLE = True + except ImportError: + LQ_CPP_BINARY_AVAILABLE = False + + +import pennylane as qml +from pennylane.devices import Device, ExecutionConfig, DefaultExecutionConfig +from pennylane.devices.modifiers import convert_single_circuit_to_batch, simulator_tracking +from pennylane.devices.preprocess import ( + decompose, + validate_device_wires, + decompose, + validate_measurements, + validate_observables, + no_sampling, +) +from pennylane.devices.qubit.sampling import get_num_shots_and_executions +from pennylane.tape import QuantumScript +from pennylane.transforms.core import TransformProgram +from pennylane.typing import Result, ResultBatch +from pennylane_lightning.lightning_qubit.device_modifiers import convert_single_circuit_to_batch + +from .device_modifiers import convert_single_circuit_to_batch + +def simulate( + circuit, + rng=None, + c_dtype=np.complex128, + batch_obs=False, + mcmc=False, + kernel_name="Local", + num_burnin=100, +): + """Calculate the results for a given circuit.""" + return 0.0 + + +def jacobian(circuit): + """Calculate the jacobian for a given circuit.""" + return np.array(0.0) + + +def simulate_and_jacobian(circuit): + """Calculate the results and jacobian for a single circuit.""" + return np.array(0.0), np.array(0.0) + + +Result_or_ResultBatch = Union[Result, ResultBatch] +QuantumTapeBatch = Sequence[qml.tape.QuantumTape] +QuantumTape_or_Batch = Union[qml.tape.QuantumTape, QuantumTapeBatch] + + +_operations = frozenset( + { + "Identity", + "BasisState", + "QubitStateVector", + "StatePrep", + "QubitUnitary", + "ControlledQubitUnitary", + "MultiControlledX", + "DiagonalQubitUnitary", + "PauliX", + "PauliY", + "PauliZ", + "MultiRZ", + "Hadamard", + "S", + "Adjoint(S)", + "T", + "Adjoint(T)", + "SX", + "Adjoint(SX)", + "CNOT", + "SWAP", + "ISWAP", + "PSWAP", + "Adjoint(ISWAP)", + "SISWAP", + "Adjoint(SISWAP)", + "SQISW", + "CSWAP", + "Toffoli", + "CY", + "CZ", + "PhaseShift", + "ControlledPhaseShift", + "CPhase", + "RX", + "RY", + "RZ", + "Rot", + "CRX", + "CRY", + "CRZ", + "C(PauliX)", + "C(PauliY)", + "C(PauliZ)", + "C(Hadamard)", + "C(S)", + "C(T)", + "C(PhaseShift)", + "C(RX)", + "C(RY)", + "C(RZ)", + "C(SWAP)", + "C(IsingXX)", + "C(IsingXY)", + "C(IsingYY)", + "C(IsingZZ)", + "C(SingleExcitation)", + "C(SingleExcitationMinus)", + "C(SingleExcitationPlus)", + "C(DoubleExcitation)", + "C(DoubleExcitationMinus)", + "C(DoubleExcitationPlus)", + "CRot", + "IsingXX", + "IsingYY", + "IsingZZ", + "IsingXY", + "SingleExcitation", + "SingleExcitationPlus", + "SingleExcitationMinus", + "DoubleExcitation", + "DoubleExcitationPlus", + "DoubleExcitationMinus", + "QubitCarry", + "QubitSum", + "OrbitalRotation", + "QFT", + "ECR", + "BlockEncode", + } +) +"""The set of supported operations.""" + +_observables = frozenset( + { + "PauliX", + "PauliY", + "PauliZ", + "Hadamard", + "Hermitian", + "Identity", + "Projector", + "SparseHamiltonian", + "Hamiltonian", + "Sum", + "SProd", + "Prod", + "Exp", + } +) +"""Test set of supported observables.""" + + +def stopping_condition(op: qml.operation.Operator) -> bool: + """A function that determines whether or not an operation is supported by ``lightning.qubit``.""" + return op.name in _operations + + +def accepted_observables(obs: qml.operation.Operator) -> bool: + """A function that determines whether or not an observable is supported by ``lightning.qubit``.""" + return obs.name in _observables + + +def accepted_analytic_measurements(m: qml.measurements.MeasurementProcess) -> bool: + """Whether or not a state based measurement is supported by ``lightning.qubit``.""" + return isinstance(m, (qml.measurements.ExpectationMP)) + + +@simulator_tracking +@convert_single_circuit_to_batch +class LightningQubit2(Device): + """PennyLane Lightning Qubit device. + + A device that interfaces with C++ to perform fast linear algebra calculations. + + Use of this device requires pre-built binaries or compilation from source. Check out the + :doc:`/lightning_qubit/installation` guide for more details. + + Args: + wires (int): the number of wires to initialize the device with + c_dtype: Datatypes for statevector representation. Must be one of + ``np.complex64`` or ``np.complex128``. + shots (int): How many times the circuit should be evaluated (or sampled) to estimate + the expectation values. Defaults to ``None`` if not specified. Setting + to ``None`` results in computing statistics like expectation values and + variances analytically. + seed (str, int, rng) + mcmc (bool): Determine whether to use the approximate Markov Chain Monte Carlo + sampling method when generating samples. + kernel_name (str): name of transition kernel. The current version supports + two kernels: ``"Local"`` and ``"NonZeroRandom"``. + The local kernel conducts a bit-flip local transition between states. + The local kernel generates a random qubit site and then generates a random + number to determine the new bit at that qubit site. The ``"NonZeroRandom"`` kernel + randomly transits between states that have nonzero probability. + num_burnin (int): number of steps that will be dropped. Increasing this value will + result in a closer approximation but increased runtime. + batch_obs (bool): Determine whether we process observables in parallel when + computing the jacobian. This value is only relevant when the lightning + qubit is built with OpenMP. + """ + + name = "lightning.qubit2" + + _device_options = ("rng", "c_dtype", "batch_obs", "mcmc", "kernel_name", "num_burnin") + + def __init__( # pylint: disable=too-many-arguments + self, + wires, + *, + c_dtype=np.complex128, + shots=None, + seed="global", + mcmc=False, + kernel_name="Local", + num_burnin=100, + batch_obs=False, + ): + if not LQ_CPP_BINARY_AVAILABLE: + raise ImportError("Pre-compiled binaries for lightning.qubit are not available. " + "To manually compile from source, follow the instructions at " + "https://pennylane-lightning.readthedocs.io/en/latest/installation.html.") + super().__init__(wires=wires, shots=shots) + seed = np.random.randint(0, high=10000000) if seed == "global" else seed + self._rng = np.random.default_rng(seed) + + self._c_dtype = c_dtype + self._batch_obs = batch_obs + self._mcmc = mcmc + if self._mcmc: + if kernel_name not in [ + "Local", + "NonZeroRandom", + ]: + raise NotImplementedError( + f"The {kernel_name} is not supported and currently " + "only 'Local' and 'NonZeroRandom' kernels are supported." + ) + if num_burnin >= shots: + raise ValueError("Shots should be greater than num_burnin.") + self._kernel_name = kernel_name + self._num_burnin = num_burnin + else: + self._kernel_name = None + self._num_burnin = None + + @property + def c_dtype(self): + """State vector complex data type.""" + return self._c_dtype + + @property + def operation(self) -> frozenset[str]: + """The names of the supported operations.""" + return _operations + + @property + def observables(self) -> frozenset[str]: + """The names of the supported observables.""" + return _observables + + def _setup_execution_config(self, config): + """ + Update the execution config with choices for how the device should be used and the device options. + """ + updated_values = {} + if config.gradient_method == "best": + updated_values["gradient_method"] = "adjoint" + if config.use_device_gradient is None: + updated_values["use_device_gradient"] = config.gradient_method in ("best", "adjoint") + if config.grad_on_execution is None: + updated_values["grad_on_execution"] = True + + new_device_options = dict(config.device_options) + for option in self._device_options: + if option not in new_device_options: + new_device_options[option] = getattr(self, f"_{option}", None) + + return replace(config, **updated_values, device_options=new_device_options) + + def supports_derivatives( + self, + execution_config: Optional[ExecutionConfig] = None, + circuit: Optional[qml.tape.QuantumTape] = None, + ) -> bool: + if execution_config is None and circuit is None: + return True + if execution_config.gradient_method not in {"adjoint", "best"}: + return False + if circuit is None: + return True + return ( + all(isinstance(m, qml.measurements.ExpectationMP) for m in circuit.measurements) + and not circuit.shots + ) + + def preprocess(self, execution_config: ExecutionConfig = DefaultExecutionConfig): + program = TransformProgram() + program.add_transform( + validate_measurements, analytic_measurements=accepted_analytic_measurements, name=self.name + ) + program.add_transform(no_sampling) + program.add_transform(validate_observables, accepted_observables, name=self.name) + program.add_transform(validate_device_wires, self.wires, name=self.name) + program.add_transform(qml.defer_measurements, device=self) + program.add_transform(decompose, stopping_condition=stopping_condition, name=self.name) + program.add_transform(qml.transforms.broadcast_expand) + return program, self._setup_execution_config(execution_config) + + def execute( + self, + circuits: QuantumTape_or_Batch, + execution_config: ExecutionConfig = DefaultExecutionConfig, + ) -> Result_or_ResultBatch: + + results = [] + for circuit in circuits: + circuit = circuit.map_to_standard_wires() + results.append(simulate(circuit, **execution_config.device_options)) + + return tuple(results) + + def compute_derivatives( + self, + circuits: QuantumTape_or_Batch, + execution_config: ExecutionConfig = DefaultExecutionConfig, + ): + return tuple(jacobian(circuit) for circuit in circuits) + + def execute_and_compute_derivatives( + self, + circuits: QuantumTape_or_Batch, + execution_config: ExecutionConfig = DefaultExecutionConfig, + ): + results = tuple(simulate_and_jacobian(c) for c in circuits) + return tuple(zip(*results)) From 3a93d1ddc3c5db632cce63f19fdcff8ae85450ac Mon Sep 17 00:00:00 2001 From: albi3ro Date: Fri, 16 Feb 2024 13:59:21 -0500 Subject: [PATCH 120/428] adding tests from add-simulate branch --- tests/conftest.py | 15 +- tests/lightning_qubit2/test_expval_2.py | 412 +++++++++++++++ tests/lightning_qubit2/test_serialize_2.py | 563 +++++++++++++++++++++ 3 files changed, 985 insertions(+), 5 deletions(-) create mode 100644 tests/lightning_qubit2/test_expval_2.py create mode 100644 tests/lightning_qubit2/test_serialize_2.py diff --git a/tests/conftest.py b/tests/conftest.py index 96531bf421..dd1fecc795 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -87,7 +87,7 @@ def n_subsystems(request): # Looking for the device for testing. default_device = "lightning.qubit" -supported_devices = {"lightning.kokkos", "lightning.qubit", "lightning.gpu"} +supported_devices = {"lightning.kokkos", "lightning.qubit", "lightning.qubit2", "lightning.gpu"} supported_devices.update({sb.replace(".", "_") for sb in supported_devices}) @@ -113,10 +113,10 @@ def get_device(): device_name = get_device() -if device_name not in qml.plugin_devices: - raise qml.DeviceError( - f"Device {device_name} does not exist. Make sure the required plugin is installed." - ) +# if device_name not in qml.plugin_devices: +# raise qml.DeviceError( +# f"Device {device_name} does not exist. Make sure the required plugin is installed." +# ) # Device specification import pennylane_lightning.lightning_qubit as lightning_ops # Any definition of lightning_ops will do @@ -131,6 +131,11 @@ def get_device(): if hasattr(pennylane_lightning, "lightning_gpu_ops"): import pennylane_lightning.lightning_gpu_ops as lightning_ops +elif device_name == "lightning.qubit2": + from pennylane_lightning.lightning_qubit import LightningQubit2 as LightningDevice + + if hasattr(pennylane_lightning, "lightning_qubit_ops"): + import pennylane_lightning.lightning_qubit_ops as lightning_ops else: from pennylane_lightning.lightning_qubit import LightningQubit as LightningDevice diff --git a/tests/lightning_qubit2/test_expval_2.py b/tests/lightning_qubit2/test_expval_2.py new file mode 100644 index 0000000000..78553c3e44 --- /dev/null +++ b/tests/lightning_qubit2/test_expval_2.py @@ -0,0 +1,412 @@ +# Copyright 2018-2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +Tests for process and execute (expval calculation). +""" +import pytest + +import numpy as np +import pennylane as qml +from pennylane_lightning.lightning_qubit import LightningQubit2 as LightningQubit +from pennylane.devices import DefaultQubit + +from conftest import LightningDevice # tested device + +if not LightningQubit._CPP_BINARY_AVAILABLE: + pytest.skip("No binary module found. Skipping.", allow_module_level=True) + +if LightningDevice != LightningQubit: + pytest.skip("Exclusive tests for lightning.qubit. Skipping.", allow_module_level=True) + +THETA = np.linspace(0.11, 1, 3) +PHI = np.linspace(0.32, 1, 3) +VARPHI = np.linspace(0.02, 1, 3) + + +@pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI))) +class TestExpval: + """Test expectation value calculations""" + + @pytest.fixture(params=[np.complex64, np.complex128]) + def dev(self, request): + return LightningQubit(c_dtype=request.param) + + @staticmethod + def calculate_reference(tape): + dev = DefaultQubit(max_workers=1) + program, _ = dev.preprocess() + tapes, transf_fn = program([tape]) + results = dev.execute(tapes) + return transf_fn(results) + + @staticmethod + def process_and_execute(dev, tape): + program, _ = dev.preprocess() + tapes, transf_fn = program([tape]) + results = dev.execute(tapes) + return transf_fn(results) + + def test_Identity(self, theta, phi, dev, tol): + """Tests applying identities.""" + + with qml.tape.QuantumTape() as tape: + qml.Identity(wires=[0]) + qml.Identity(wires=[0, 1]) + qml.Identity(wires=[1, 2]) + qml.RX(theta, wires=[0]) + qml.RX(phi, wires=[1]) + qml.expval(qml.PauliX(0)) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_identity_expectation(self, theta, phi, dev, tol): + """Tests identity.""" + + tape = qml.tape.QuantumScript( + [qml.RX(theta, wires=[0]), qml.RX(phi, wires=[1]), qml.CNOT(wires=[0, 1])], + [qml.expval(qml.Identity(wires=[0])), qml.expval(qml.Identity(wires=[1]))], + ) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_multi_wire_identity_expectation(self, theta, phi, dev, tol): + """Tests multi-wire identity.""" + + tape = qml.tape.QuantumScript( + [qml.RX(theta, wires=[0]), qml.RX(phi, wires=[1]), qml.CNOT(wires=[0, 1])], + [qml.expval(qml.Identity(wires=[0, 1]))], + ) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + @pytest.mark.parametrize( + "wires", + [ + ([0, 1]), + (["a", 1]), + (["b", "a"]), + ], + ) + def test_PauliZ_expectation(self, theta, phi, dev, tol, wires): + """Tests PauliZ.""" + + tape = qml.tape.QuantumScript( + [qml.RX(theta, wires=wires[0]), qml.RX(phi, wires=wires[1]), qml.CNOT(wires=wires)], + [qml.expval(qml.PauliZ(wires=wires[0])), qml.expval(qml.PauliZ(wires=wires[1]))], + ) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_PauliX_expectation(self, theta, phi, dev, tol): + """Tests PauliX.""" + + tape = qml.tape.QuantumScript( + [qml.RY(theta, wires=[0]), qml.RY(phi, wires=[1]), qml.CNOT(wires=[0, 1])], + [qml.expval(qml.PauliX(wires=[0])), qml.expval(qml.PauliX(wires=[1]))], + ) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_PauliY_expectation(self, theta, phi, dev, tol): + """Tests PauliY.""" + + tape = qml.tape.QuantumScript( + [qml.RX(theta, wires=[0]), qml.RX(phi, wires=[1]), qml.CNOT(wires=[0, 1])], + [qml.expval(qml.PauliY(wires=[0])), qml.expval(qml.PauliY(wires=[1]))], + ) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_hadamard_expectation(self, theta, phi, dev, tol): + """Tests Hadamard.""" + + tape = qml.tape.QuantumScript( + [qml.RY(theta, wires=[0]), qml.RY(phi, wires=[1]), qml.CNOT(wires=[0, 1])], + [qml.expval(qml.Hadamard(wires=[0])), qml.expval(qml.Hadamard(wires=[1]))], + ) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_hermitian_expectation(self, theta, phi, dev, tol): + """Tests an Hermitian operator.""" + + with qml.tape.QuantumTape() as tape: + qml.RX(theta, wires=0) + qml.RX(phi, wires=1) + qml.RX(theta + phi, wires=2) + + for idx in range(3): + qml.expval(qml.Hermitian([[1, 0], [0, -1]], wires=[idx])) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_hamiltonian_expectation(self, theta, phi, dev, tol): + """Tests a Hamiltonian.""" + + ham = qml.Hamiltonian( + [1.0, 0.3, 0.3, 0.4], + [ + qml.PauliX(0) @ qml.PauliX(1), + qml.PauliZ(0), + qml.PauliZ(1), + qml.PauliX(0) @ qml.PauliY(1), + ], + ) + + with qml.tape.QuantumTape() as tape: + qml.RX(theta, wires=0) + qml.RX(phi, wires=1) + qml.RX(theta + phi, wires=2) + + qml.expval(ham) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_sparse_hamiltonian_expectation(self, theta, phi, dev, tol): + """Tests a Hamiltonian.""" + + ham = qml.SparseHamiltonian( + qml.Hamiltonian( + [1.0, 0.3, 0.3, 0.4], + [ + qml.PauliX(0) @ qml.PauliX(1), + qml.PauliZ(0), + qml.PauliZ(1), + qml.PauliX(0) @ qml.PauliY(1), + ], + ).sparse_matrix(), + wires=[0, 1], + ) + + with qml.tape.QuantumTape() as tape: + qml.RX(theta, wires=0) + qml.RX(phi, wires=1) + + qml.expval(ham) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + +@pytest.mark.parametrize("phi", PHI) +class TestOperatorArithmetic: + """Test integration with SProd, Prod, and Sum.""" + + @pytest.fixture(params=[np.complex64, np.complex128]) + def dev(self, request): + return LightningQubit(c_dtype=request.param) + + @staticmethod + def calculate_reference(tape): + dev = DefaultQubit(max_workers=1) + program, _ = dev.preprocess() + tapes, transf_fn = program([tape]) + results = dev.execute(tapes) + return transf_fn(results) + + @staticmethod + def process_and_execute(dev, tape): + program, _ = dev.preprocess() + tapes, transf_fn = program([tape]) + results = dev.execute(tapes) + return transf_fn(results) + + def test_s_prod(self, phi, dev, tol): + """Tests the `SProd` class.""" + + tape = qml.tape.QuantumScript( + [qml.RX(phi, wires=[0])], + [qml.expval(qml.s_prod(0.5, qml.PauliZ(0)))], + ) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_prod(self, phi, dev, tol): + """Tests the `Prod` class.""" + + tape = qml.tape.QuantumScript( + [qml.RX(phi, wires=[0]), qml.Hadamard(wires=[1]), qml.PauliZ(wires=[1])], + [qml.expval(qml.prod(qml.PauliZ(0), qml.PauliX(1)))], + ) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_sum(self, phi, dev, tol): + """Tests the `Sum` class.""" + tape = qml.tape.QuantumScript( + [qml.RX(phi, wires=[0]), qml.RX(-1.1 * phi, wires=[0])], + [qml.expval(qml.sum(qml.PauliZ(0), qml.PauliX(1)))], + ) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_integration(self, phi, dev, tol): + """Test a Combination of `Sum`, `SProd`, and `Prod`.""" + + obs = qml.sum(qml.s_prod(2.3, qml.PauliZ(0)), -0.5 * qml.prod(qml.PauliY(0), qml.PauliZ(1))) + + tape = qml.tape.QuantumScript( + [qml.RX(phi, wires=[0]), qml.RX(-1.1 * phi, wires=[0])], + [qml.expval(obs)], + ) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + +@pytest.mark.parametrize("theta, phi, varphi", list(zip(THETA, PHI, VARPHI))) +class TestTensorExpval: + """Test tensor expectation values""" + + @pytest.fixture(params=[np.complex64, np.complex128]) + def dev(self, request): + return LightningQubit(c_dtype=request.param) + + @staticmethod + def calculate_reference(tape): + dev = DefaultQubit(max_workers=1) + program, _ = dev.preprocess() + tapes, transf_fn = program([tape]) + results = dev.execute(tapes) + return transf_fn(results) + + @staticmethod + def process_and_execute(dev, tape): + program, _ = dev.preprocess() + tapes, transf_fn = program([tape]) + results = dev.execute(tapes) + return transf_fn(results) + + def test_PauliX_PauliY(self, theta, phi, varphi, dev, tol): + """Tests a tensor product involving PauliX and PauliY.""" + + with qml.tape.QuantumTape() as tape: + qml.RX(theta, wires=[0]) + qml.RX(phi, wires=[1]) + qml.RX(varphi, wires=[2]) + qml.CNOT(wires=[0, 1]) + qml.CNOT(wires=[1, 2]) + qml.expval(qml.PauliX(0) @ qml.PauliY(2)) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_PauliZ_identity(self, theta, phi, varphi, dev, tol): + """Tests a tensor product involving PauliZ and Identity.""" + + with qml.tape.QuantumTape() as tape: + qml.Identity(wires=[0]) + qml.RX(theta, wires=[0]) + qml.RX(phi, wires=[1]) + qml.RX(varphi, wires=[2]) + qml.CNOT(wires=[0, 1]) + qml.CNOT(wires=[1, 2]) + qml.expval(qml.PauliZ(0) @ qml.Identity(1) @ qml.PauliZ(2)) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_PauliZ_hadamard_PauliY(self, theta, phi, varphi, dev, tol): + """Tests a tensor product involving PauliY, PauliZ and Hadamard.""" + + with qml.tape.QuantumTape() as tape: + qml.RX(theta, wires=[0]) + qml.RX(phi, wires=[1]) + qml.RX(varphi, wires=[2]) + qml.CNOT(wires=[0, 1]) + qml.CNOT(wires=[1, 2]) + qml.expval(qml.PauliZ(0) @ qml.Hadamard(1) @ qml.PauliY(2)) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) diff --git a/tests/lightning_qubit2/test_serialize_2.py b/tests/lightning_qubit2/test_serialize_2.py new file mode 100644 index 0000000000..7f14422c38 --- /dev/null +++ b/tests/lightning_qubit2/test_serialize_2.py @@ -0,0 +1,563 @@ +# Copyright 2018-2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +Unit tests for the serialization helper functions. +""" +import pytest +from conftest import device_name, LightningDevice + +import numpy as np +import pennylane as qml +from pennylane_lightning.lightning_qubit._serialize import QuantumScriptSerializer + +# from pennylane_lightning.lightning_qubit._serialize_old import QuantumScriptSerializer + +if not LightningDevice._CPP_BINARY_AVAILABLE: + pytest.skip("No binary module found. Skipping.", allow_module_level=True) + +if device_name == "lightning.kokkos": + from pennylane_lightning.lightning_kokkos_ops.observables import ( + NamedObsC64, + NamedObsC128, + HermitianObsC64, + HermitianObsC128, + TensorProdObsC64, + TensorProdObsC128, + HamiltonianC64, + HamiltonianC128, + SparseHamiltonianC64, + SparseHamiltonianC128, + ) +elif device_name == "lightning.gpu": + from pennylane_lightning.lightning_gpu_ops.observables import ( + NamedObsC64, + NamedObsC128, + HermitianObsC64, + HermitianObsC128, + TensorProdObsC64, + TensorProdObsC128, + HamiltonianC64, + HamiltonianC128, + SparseHamiltonianC64, + SparseHamiltonianC128, + ) +else: + from pennylane_lightning.lightning_qubit_ops.observables import ( + NamedObsC64, + NamedObsC128, + HermitianObsC64, + HermitianObsC128, + TensorProdObsC64, + TensorProdObsC128, + HamiltonianC64, + HamiltonianC128, + SparseHamiltonianC64, + SparseHamiltonianC128, + ) + + +def test_wrong_device_name(): + """Test the device name is not a valid option""" + + with pytest.raises(qml.DeviceError, match="The device name"): + QuantumScriptSerializer("thunder.qubit") + + +@pytest.mark.parametrize( + "obs,obs_type", + [ + (qml.PauliZ(0), NamedObsC128), + (qml.PauliZ(0) @ qml.PauliZ(1), TensorProdObsC128), + (qml.Hadamard(0), NamedObsC128), + (qml.Hermitian(np.eye(2), wires=0), HermitianObsC128), + ( + qml.PauliZ(0) @ qml.Hadamard(1) @ (0.1 * (qml.PauliZ(2) + qml.PauliX(3))), + HamiltonianC128, + ), + ( + ( + qml.Hermitian(np.eye(2), wires=0) + @ qml.Hermitian(np.eye(2), wires=1) + @ qml.Projector([0], wires=2) + ), + TensorProdObsC128, + ), + ( + qml.PauliZ(0) @ qml.Hermitian(np.eye(2), wires=1) @ qml.Projector([0], wires=2), + TensorProdObsC128, + ), + (qml.Projector([0], wires=0), HermitianObsC128), + (qml.Hamiltonian([1], [qml.PauliZ(0)]), HamiltonianC128), + (qml.sum(qml.Hadamard(0), qml.PauliX(1)), HermitianObsC128), + ( + qml.SparseHamiltonian(qml.Hamiltonian([1], [qml.PauliZ(0)]).sparse_matrix(), wires=[0]), + SparseHamiltonianC128, + ), + ], +) +def test_obs_returns_expected_type(obs, obs_type): + """Tests that observables get serialized to the expected type.""" + assert isinstance(QuantumScriptSerializer(device_name)._ob(obs), obs_type) + + +class TestSerializeObs: + """Tests for the serialize_observables function""" + + @pytest.mark.parametrize("use_csingle", [True, False]) + def test_tensor_non_tensor_return(self, use_csingle): + """Test expected serialization for a mixture of tensor product and non-tensor product + return""" + with qml.tape.QuantumTape() as tape: + qml.expval(qml.PauliZ(0) @ qml.PauliX(1)) + qml.expval(qml.Hadamard(1)) + + tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 + named_obs = NamedObsC64 if use_csingle else NamedObsC128 + + s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) + + s_expected = [ + tensor_prod_obs([named_obs("PauliZ", [0]), named_obs("PauliX", [1])]), + named_obs("Hadamard", [1]), + ] + + assert s == s_expected + + @pytest.mark.parametrize("use_csingle", [True, False]) + def test_hermitian_return(self, use_csingle): + """Test expected serialization for a Hermitian return""" + with qml.tape.QuantumTape() as tape: + qml.expval(qml.Hermitian(np.eye(4), wires=[0, 1])) + + hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 + c_dtype = np.complex64 if use_csingle else np.complex128 + + s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) + s_expected = hermitian_obs( + np.array( + [1.0, 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, 1.0], + dtype=c_dtype, + ), + [0, 1], + ) + assert s[0] == s_expected + + @pytest.mark.parametrize("use_csingle", [True, False]) + def test_hermitian_tensor_return(self, use_csingle): + """Test expected serialization for a Hermitian return""" + with qml.tape.QuantumTape() as tape: + qml.expval(qml.Hermitian(np.eye(4), wires=[0, 1]) @ qml.Hermitian(np.eye(2), wires=[2])) + + c_dtype = np.complex64 if use_csingle else np.complex128 + tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 + hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 + s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) + + s_expected = tensor_prod_obs( + [ + hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), + hermitian_obs(np.eye(2, dtype=c_dtype).ravel(), [2]), + ] + ) + + assert s[0] == s_expected + + @pytest.mark.parametrize("use_csingle", [True, False]) + def test_mixed_tensor_return(self, use_csingle): + """Test expected serialization for a mixture of Hermitian and Pauli return""" + with qml.tape.QuantumTape() as tape: + qml.expval(qml.Hermitian(np.eye(4), wires=[0, 1]) @ qml.PauliY(2)) + + c_dtype = np.complex64 if use_csingle else np.complex128 + tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 + hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 + named_obs = NamedObsC64 if use_csingle else NamedObsC128 + + s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) + + s_expected = tensor_prod_obs( + [hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), named_obs("PauliY", [2])] + ) + + assert s[0] == s_expected + + @pytest.mark.parametrize("use_csingle", [True, False]) + def test_hamiltonian_return(self, use_csingle): + """Test expected serialization for a Hamiltonian return""" + + ham = qml.Hamiltonian( + [0.3, 0.5, 0.4], + [ + qml.Hermitian(np.eye(4), wires=[0, 1]) @ qml.PauliY(2), + qml.PauliX(0) @ qml.PauliY(2), + qml.Hermitian(np.ones((8, 8)), wires=range(3)), + ], + ) + + with qml.tape.QuantumTape() as tape: + qml.expval(ham) + + hamiltonian_obs = HamiltonianC64 if use_csingle else HamiltonianC128 + named_obs = NamedObsC64 if use_csingle else NamedObsC128 + hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 + tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 + r_dtype = np.float32 if use_csingle else np.float64 + c_dtype = np.complex64 if use_csingle else np.complex128 + + s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) + + s_expected = hamiltonian_obs( + np.array([0.3, 0.5, 0.4], dtype=r_dtype), + [ + tensor_prod_obs( + [ + hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), + named_obs("PauliY", [2]), + ] + ), + tensor_prod_obs([named_obs("PauliX", [0]), named_obs("PauliY", [2])]), + hermitian_obs(np.ones(64, dtype=c_dtype), [0, 1, 2]), + ], + ) + + assert s[0] == s_expected + + @pytest.mark.parametrize("use_csingle", [True, False]) + def test_hamiltonian_tensor_return(self, use_csingle): + """Test expected serialization for a Hamiltonian return""" + + with qml.tape.QuantumTape() as tape: + ham = qml.Hamiltonian( + [0.3, 0.5, 0.4], + [ + qml.Hermitian(np.eye(4), wires=[0, 1]) @ qml.PauliY(2), + qml.PauliX(0) @ qml.PauliY(2), + qml.Hermitian(np.ones((8, 8)), wires=range(3)), + ], + ) + qml.expval(ham @ qml.PauliZ(3)) + + hamiltonian_obs = HamiltonianC64 if use_csingle else HamiltonianC128 + named_obs = NamedObsC64 if use_csingle else NamedObsC128 + hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 + tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 + r_dtype = np.float32 if use_csingle else np.float64 + c_dtype = np.complex64 if use_csingle else np.complex128 + + s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) + + # Expression (ham @ obs) is converted internally by Pennylane + # where obs is appended to each term of the ham + s_expected = hamiltonian_obs( + np.array([0.3, 0.5, 0.4], dtype=r_dtype), + [ + tensor_prod_obs( + [ + hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), + named_obs("PauliY", [2]), + named_obs("PauliZ", [3]), + ] + ), + tensor_prod_obs( + [named_obs("PauliX", [0]), named_obs("PauliY", [2]), named_obs("PauliZ", [3])] + ), + tensor_prod_obs( + [hermitian_obs(np.ones(64, dtype=c_dtype), [0, 1, 2]), named_obs("PauliZ", [3])] + ), + ], + ) + + assert s[0] == s_expected + + @pytest.mark.parametrize("use_csingle", [True, False]) + def test_hamiltonian_mix_return(self, use_csingle): + """Test expected serialization for a Hamiltonian return""" + + ham1 = qml.Hamiltonian( + [0.3, 0.5, 0.4], + [ + qml.Hermitian(np.eye(4), wires=[0, 1]) @ qml.PauliY(2), + qml.PauliX(0) @ qml.PauliY(2), + qml.Hermitian(np.ones((8, 8)), wires=range(3)), + ], + ) + ham2 = qml.Hamiltonian( + [0.7, 0.3], + [qml.PauliX(0) @ qml.Hermitian(np.eye(4), wires=[1, 2]), qml.PauliY(0) @ qml.PauliX(2)], + ) + + with qml.tape.QuantumTape() as tape: + qml.expval(ham1) + qml.expval(ham2) + + hamiltonian_obs = HamiltonianC64 if use_csingle else HamiltonianC128 + named_obs = NamedObsC64 if use_csingle else NamedObsC128 + hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 + tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 + r_dtype = np.float32 if use_csingle else np.float64 + c_dtype = np.complex64 if use_csingle else np.complex128 + + s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) + + s_expected1 = hamiltonian_obs( + np.array([0.3, 0.5, 0.4], dtype=r_dtype), + [ + tensor_prod_obs( + [ + hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), + named_obs("PauliY", [2]), + ] + ), + tensor_prod_obs([named_obs("PauliX", [0]), named_obs("PauliY", [2])]), + hermitian_obs(np.ones(64, dtype=c_dtype), [0, 1, 2]), + ], + ) + s_expected2 = hamiltonian_obs( + np.array([0.7, 0.3], dtype=r_dtype), + [ + tensor_prod_obs( + [ + named_obs("PauliX", [0]), + hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [1, 2]), + ] + ), + tensor_prod_obs([named_obs("PauliY", [0]), named_obs("PauliX", [2])]), + ], + ) + + assert s[0] == s_expected1 + assert s[1] == s_expected2 + + @pytest.mark.parametrize( + "obs,coeffs,terms", + [ + (qml.prod(qml.PauliZ(0), qml.PauliX(1)), [1], [[("PauliX", 1), ("PauliZ", 0)]]), + (qml.s_prod(0.1, qml.PauliX(0)), [0.1], ("PauliX", 0)), + ( + qml.sum( + 0.5 * qml.prod(qml.PauliX(0), qml.PauliZ(1)), + 0.1 * qml.prod(qml.PauliZ(0), qml.PauliY(1)), + ), + [0.5, 0.1], + [[("PauliZ", 1), ("PauliX", 0)], [("PauliY", 1), ("PauliZ", 0)]], + ), + ], + ) + @pytest.mark.parametrize("use_csingle", [True, False]) + def test_op_arithmetic_uses_hamiltonian(self, use_csingle, obs, coeffs, terms): + """Tests that an arithmetic obs with a PauliRep serializes as a Hamiltonian.""" + tape = qml.tape.QuantumTape(measurements=[qml.expval(obs)]) + res, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) + assert len(res) == 1 + assert isinstance(res[0], HamiltonianC64 if use_csingle else HamiltonianC128) + + hamiltonian_obs = HamiltonianC64 if use_csingle else HamiltonianC128 + tensor_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 + named_obs = NamedObsC64 if use_csingle else NamedObsC128 + rtype = np.float32 if use_csingle else np.float64 + term_shape = np.array(terms).shape + + if len(term_shape) == 1: # just a single pauli op + expected_terms = [named_obs(terms[0], [terms[1]])] + elif len(term_shape) == 3: # list of tensor products + expected_terms = [ + tensor_obs([named_obs(pauli, [wire]) for pauli, wire in term]) for term in terms + ] + + coeffs = np.array(coeffs).astype(rtype) + assert res[0] == hamiltonian_obs(coeffs, expected_terms) + + @pytest.mark.parametrize("use_csingle", [True, False]) + def test_multi_wire_identity(self, use_csingle): + """Tests that multi-wire Identity does not fail serialization.""" + tape = qml.tape.QuantumTape(measurements=[qml.expval(qml.Identity(wires=[1, 2]))]) + res, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) + assert len(res) == 1 + + named_obs = NamedObsC64 if use_csingle else NamedObsC128 + assert res[0] == named_obs("Identity", [1]) + + +class TestSerializeOps: + """Tests for the _ops function""" + + def test_basic_circuit(self): + """Test expected serialization for a simple circuit""" + with qml.tape.QuantumTape() as tape: + qml.RX(0.4, wires=0) + qml.RY(0.6, wires=1) + qml.CNOT(wires=[0, 1]) + + s = QuantumScriptSerializer(device_name).serialize_ops(tape) + s_expected = ( + ( + ["RX", "RY", "CNOT"], + [np.array([0.4]), np.array([0.6]), []], + [[0], [1], [0, 1]], + [False, False, False], + [[], [], []], + [[], [], []], + [[], [], []], + ), + False, + ) + assert s == s_expected + + def test_basic_circuit_not_implemented_ctrl_ops(self): + """Test expected serialization for a simple circuit""" + ops = qml.OrbitalRotation(0.1234, wires=range(4)) + with qml.tape.QuantumTape() as tape: + qml.RX(0.4, wires=0) + qml.RY(0.6, wires=1) + qml.ctrl(ops, [4, 5]) + + s = QuantumScriptSerializer(device_name).serialize_ops(tape) + s_expected = ( + ( + ["RX", "RY", "QubitUnitary"], + [np.array([0.4]), np.array([0.6]), [0.0]], + [[0], [1], list(ops.wires)], + [False, False, False], + [[], [], [qml.matrix(ops)]], + [[], [], [4, 5]], + ), + False, + ) + assert s[0][0] == s_expected[0][0] + assert s[0][1] == s_expected[0][1] + assert s[0][2] == s_expected[0][2] + assert s[0][3] == s_expected[0][3] + assert all(np.allclose(s0, s1) for s0, s1 in zip(s[0][4], s_expected[0][4])) + assert s[0][5] == s_expected[0][5] + assert s[1] == s_expected[1] + + def test_multicontrolledx(self): + """Test expected serialization for a simple circuit""" + with qml.tape.QuantumTape() as tape: + qml.RX(0.4, wires=0) + qml.RY(0.6, wires=1) + qml.ctrl(qml.PauliX(wires=0), [1, 2, 3], control_values=[True, False, False]) + + s = QuantumScriptSerializer(device_name).serialize_ops(tape) + s_expected = ( + ( + ["RX", "RY", "PauliX"], + [np.array([0.4]), np.array([0.6]), []], + [[0], [1], [0]], + [False, False, False], + [[], [], []], + [[], [], [1, 2, 3]], + [[], [], [True, False, False]], + ), + False, + ) + assert s == s_expected + + @pytest.mark.parametrize("stateprep", [qml.QubitStateVector, qml.StatePrep]) + def test_skips_prep_circuit(self, stateprep): + """Test expected serialization for a simple circuit with state preparation, such that + the state preparation is skipped""" + with qml.tape.QuantumTape() as tape: + stateprep([1, 0], wires=0) + qml.BasisState([1], wires=1) + qml.RX(0.4, wires=0) + qml.RY(0.6, wires=1) + qml.CNOT(wires=[0, 1]) + + s = QuantumScriptSerializer(device_name).serialize_ops(tape) + s_expected = ( + ( + ["RX", "RY", "CNOT"], + [[0.4], [0.6], []], + [[0], [1], [0, 1]], + [False, False, False], + [[], [], []], + [[], [], []], + [[], [], []], + ), + True, + ) + assert s == s_expected + + def test_unsupported_kernel_circuit(self): + """Test expected serialization for a circuit including gates that do not have a dedicated + kernel""" + with qml.tape.QuantumTape() as tape: + qml.CNOT(wires=[0, 1]) + qml.RZ(0.2, wires=2) + + s = QuantumScriptSerializer(device_name).serialize_ops(tape) + s_expected = ( + ( + ["CNOT", "RZ"], + [[], [0.2]], + [[0, 1], [2]], + [False, False], + ), + False, + ) + assert s[0][0] == s_expected[0][0] + assert s[0][1] == s_expected[0][1] + + @pytest.mark.parametrize("C", [True, False]) + def test_integration(self, C): + """Test expected serialization for a random circuit""" + with qml.tape.QuantumTape() as tape: + qml.RX(0.4, wires=0) + qml.RY(0.6, wires=1) + qml.CNOT(wires=[0, 1]) + qml.QubitUnitary(np.eye(4), wires=[0, 1]) + qml.templates.QFT(wires=[0, 1, 2]) + qml.DoubleExcitation(0.555, wires=[3, 2, 1, 0]) + qml.DoubleExcitationMinus(0.555, wires=[0, 1, 2, 3]) + qml.DoubleExcitationPlus(0.555, wires=[0, 1, 2, 3]) + + s = QuantumScriptSerializer(device_name).serialize_ops(tape) + + dtype = np.complex64 if C else np.complex128 + s_expected = ( + ( + [ + "RX", + "RY", + "CNOT", + "QubitUnitary", + "QFT", + "DoubleExcitation", + "DoubleExcitationMinus", + "DoubleExcitationPlus", + ], + [[0.4], [0.6], [], [0.0], [], [0.555], [0.555], [0.555]], + [[0], [1], [0, 1], [0, 1], [0, 1, 2], [3, 2, 1, 0], [0, 1, 2, 3], [0, 1, 2, 3]], + [False, False, False, False, False, False, False, False], + [ + [], + [], + [], + qml.matrix(qml.QubitUnitary(np.eye(4, dtype=dtype), wires=[0, 1])), + qml.matrix(qml.templates.QFT(wires=[0, 1, 2])), + [], + [], + [], + ], + ), + False, + ) + assert s[0][0] == s_expected[0][0] + assert s[0][1] == s_expected[0][1] + assert s[0][2] == s_expected[0][2] + assert s[0][3] == s_expected[0][3] + assert s[1] == s_expected[1] + + assert all(np.allclose(s1, s2) for s1, s2 in zip(s[0][4], s_expected[0][4])) From d172e611777e682aee7645a6e182ded542defce9 Mon Sep 17 00:00:00 2001 From: albi3ro Date: Fri, 16 Feb 2024 15:08:00 -0500 Subject: [PATCH 121/428] merge conflicts --- .../lightning_qubit/lightning_qubit2.py | 63 ++++++++++++++----- 1 file changed, 47 insertions(+), 16 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index cf32b7f178..507c7600d5 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -15,23 +15,10 @@ This module contains the LightningQubit2 class that inherits from the new device interface. """ -from typing import Union, Sequence, Optional +from typing import Optional, Union, Sequence, Callable from dataclasses import replace import numpy as np - -try: - # pylint: disable=import-error, no-name-in-module - from pennylane_lightning.lightning_qubit_ops import ( - StateVectorC64, - StateVectorC128, - ) - - LQ_CPP_BINARY_AVAILABLE = True - except ImportError: - LQ_CPP_BINARY_AVAILABLE = False - - import pennylane as qml from pennylane.devices import Device, ExecutionConfig, DefaultExecutionConfig from pennylane.devices.modifiers import convert_single_circuit_to_batch, simulator_tracking @@ -43,8 +30,7 @@ validate_observables, no_sampling, ) -from pennylane.devices.qubit.sampling import get_num_shots_and_executions -from pennylane.tape import QuantumScript +from pennylane.tape import QuantumTape from pennylane.transforms.core import TransformProgram from pennylane.typing import Result, ResultBatch from pennylane_lightning.lightning_qubit.device_modifiers import convert_single_circuit_to_batch @@ -63,7 +49,14 @@ def simulate( """Calculate the results for a given circuit.""" return 0.0 +try: + # pylint: disable=import-error, no-name-in-module + from pennylane_lightning.lightning_qubit_ops import ( + StateVectorC64, + StateVectorC128, + ) +<<<<<<< HEAD def jacobian(circuit): """Calculate the jacobian for a given circuit.""" return np.array(0.0) @@ -79,6 +72,44 @@ def simulate_and_jacobian(circuit): QuantumTape_or_Batch = Union[qml.tape.QuantumTape, QuantumTapeBatch] +======= + LQ_CPP_BINARY_AVAILABLE = True +except ImportError: + LQ_CPP_BINARY_AVAILABLE = False + +Result_or_ResultBatch = Union[Result, ResultBatch] +QuantumTapeBatch = Sequence[QuantumTape] +QuantumTape_or_Batch = Union[QuantumTape, QuantumTapeBatch] +PostprocessingFn = Callable[[ResultBatch], Result_or_ResultBatch] + + +def simulate(circuit: QuantumTape, dtype=np.complex128) -> Result: + """Simulate a single quantum script.a + + Args: + circuit (QuantumTape): The single circuit to simulate + dtype: Datatypes for state-vector representation. Must be one of + ``np.complex64`` or ``np.complex128``. + + Returns: + tuple(TensorLike): The results of the simulation + + Note that this function can return measurements for non-commuting observables simultaneously. + + """ + state = LightningStateVector(num_wires=circuit.num_wires, dtype=dtype).get_final_state(circuit) + return LightningMeasurements(state).measure_final_state(circuit) + + +def dummy_jacobian(circuit: QuantumTape): + return np.array(0.0) + + +def simulate_and_jacobian(circuit: QuantumTape): + return np.array(0.0), np.array(0.0) + + +>>>>>>> 2a8cd8da (merge conflicts) _operations = frozenset( { "Identity", From 5eb9fa482a22d992255527333d5ddf072cff58b4 Mon Sep 17 00:00:00 2001 From: albi3ro Date: Fri, 16 Feb 2024 15:51:29 -0500 Subject: [PATCH 122/428] create state vector on initialization --- .../lightning_qubit/lightning_qubit2.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index 507c7600d5..51d9b5abde 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -83,6 +83,7 @@ def simulate_and_jacobian(circuit): PostprocessingFn = Callable[[ResultBatch], Result_or_ResultBatch] +<<<<<<< HEAD def simulate(circuit: QuantumTape, dtype=np.complex128) -> Result: """Simulate a single quantum script.a @@ -90,6 +91,14 @@ def simulate(circuit: QuantumTape, dtype=np.complex128) -> Result: circuit (QuantumTape): The single circuit to simulate dtype: Datatypes for state-vector representation. Must be one of ``np.complex64`` or ``np.complex128``. +======= +def simulate(circuit: QuantumScript, state: LightningStateVector) -> Result: + """Simulate a single quantum script. + + Args: + circuit (QuantumTape): The single circuit to simulate + state (LightningStateVector): handle to Lightning state vector +>>>>>>> fde61720 (create state vector on initialization) Returns: tuple(TensorLike): The results of the simulation @@ -101,7 +110,7 @@ def simulate(circuit: QuantumTape, dtype=np.complex128) -> Result: return LightningMeasurements(state).measure_final_state(circuit) -def dummy_jacobian(circuit: QuantumTape): +def jacobian(circuit: QuantumTape): return np.array(0.0) @@ -285,6 +294,8 @@ def __init__( # pylint: disable=too-many-arguments "To manually compile from source, follow the instructions at " "https://pennylane-lightning.readthedocs.io/en/latest/installation.html.") super().__init__(wires=wires, shots=shots) + + self._statevector = LightningStateVector(num_wires=len(self.wires), dtype=c_dtype) seed = np.random.randint(0, high=10000000) if seed == "global" else seed self._rng = np.random.default_rng(seed) @@ -380,7 +391,7 @@ def execute( results = [] for circuit in circuits: circuit = circuit.map_to_standard_wires() - results.append(simulate(circuit, **execution_config.device_options)) + results.append(simulate(circuit, self._statevector)) return tuple(results) From ff1eef5b781419735b6de636ccc86e634ed14c43 Mon Sep 17 00:00:00 2001 From: albi3ro Date: Fri, 16 Feb 2024 15:52:23 -0500 Subject: [PATCH 123/428] remove import of modifier from lightning --- pennylane_lightning/lightning_qubit/lightning_qubit2.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index 51d9b5abde..7b8772a914 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -33,7 +33,6 @@ from pennylane.tape import QuantumTape from pennylane.transforms.core import TransformProgram from pennylane.typing import Result, ResultBatch -from pennylane_lightning.lightning_qubit.device_modifiers import convert_single_circuit_to_batch from .device_modifiers import convert_single_circuit_to_batch From 369700a0a4effb022183743afe953ac41c88232e Mon Sep 17 00:00:00 2001 From: Christina Lee Date: Fri, 16 Feb 2024 16:05:48 -0500 Subject: [PATCH 124/428] Update pennylane_lightning/lightning_qubit/lightning_qubit2.py --- pennylane_lightning/lightning_qubit/lightning_qubit2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index 7b8772a914..ff0e362828 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -324,7 +324,7 @@ def c_dtype(self): return self._c_dtype @property - def operation(self) -> frozenset[str]: + def operations(self) -> frozenset[str]: """The names of the supported operations.""" return _operations From a2056f1bff28f5e5dad0dc36243e08753071a8a4 Mon Sep 17 00:00:00 2001 From: albi3ro Date: Wed, 21 Feb 2024 11:20:49 -0500 Subject: [PATCH 125/428] minor test updates --- .../lightning_qubit/lightning_qubit2.py | 118 +++--------------- tests/lightning_qubit2/test_expval_2.py | 57 ++++----- 2 files changed, 46 insertions(+), 129 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index ff0e362828..2153a201ae 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -13,7 +13,6 @@ # limitations under the License. """ This module contains the LightningQubit2 class that inherits from the new device interface. - """ from typing import Optional, Union, Sequence, Callable from dataclasses import replace @@ -21,7 +20,7 @@ import pennylane as qml from pennylane.devices import Device, ExecutionConfig, DefaultExecutionConfig -from pennylane.devices.modifiers import convert_single_circuit_to_batch, simulator_tracking +from pennylane.devices.modifiers import single_tape_support, simulator_tracking from pennylane.devices.preprocess import ( decompose, validate_device_wires, @@ -30,48 +29,17 @@ validate_observables, no_sampling, ) -from pennylane.tape import QuantumTape +from pennylane.tape import QuantumTape, QuantumScript from pennylane.transforms.core import TransformProgram from pennylane.typing import Result, ResultBatch -from .device_modifiers import convert_single_circuit_to_batch - -def simulate( - circuit, - rng=None, - c_dtype=np.complex128, - batch_obs=False, - mcmc=False, - kernel_name="Local", - num_burnin=100, -): - """Calculate the results for a given circuit.""" - return 0.0 +from ._state_vector import LightningStateVector +from ._measurements import LightningMeasurements try: - # pylint: disable=import-error, no-name-in-module - from pennylane_lightning.lightning_qubit_ops import ( - StateVectorC64, - StateVectorC128, - ) - -<<<<<<< HEAD -def jacobian(circuit): - """Calculate the jacobian for a given circuit.""" - return np.array(0.0) - + # pylint: disable=import-error, unused-import + import pennylane_lightning.lightning_qubit_ops -def simulate_and_jacobian(circuit): - """Calculate the results and jacobian for a single circuit.""" - return np.array(0.0), np.array(0.0) - - -Result_or_ResultBatch = Union[Result, ResultBatch] -QuantumTapeBatch = Sequence[qml.tape.QuantumTape] -QuantumTape_or_Batch = Union[qml.tape.QuantumTape, QuantumTapeBatch] - - -======= LQ_CPP_BINARY_AVAILABLE = True except ImportError: LQ_CPP_BINARY_AVAILABLE = False @@ -82,31 +50,18 @@ def simulate_and_jacobian(circuit): PostprocessingFn = Callable[[ResultBatch], Result_or_ResultBatch] -<<<<<<< HEAD -def simulate(circuit: QuantumTape, dtype=np.complex128) -> Result: - """Simulate a single quantum script.a - - Args: - circuit (QuantumTape): The single circuit to simulate - dtype: Datatypes for state-vector representation. Must be one of - ``np.complex64`` or ``np.complex128``. -======= def simulate(circuit: QuantumScript, state: LightningStateVector) -> Result: """Simulate a single quantum script. - Args: circuit (QuantumTape): The single circuit to simulate state (LightningStateVector): handle to Lightning state vector ->>>>>>> fde61720 (create state vector on initialization) - Returns: tuple(TensorLike): The results of the simulation - Note that this function can return measurements for non-commuting observables simultaneously. - """ - state = LightningStateVector(num_wires=circuit.num_wires, dtype=dtype).get_final_state(circuit) - return LightningMeasurements(state).measure_final_state(circuit) + state.reset_state() + final_state = state.get_final_state(circuit) + return LightningMeasurements(final_state).measure_final_state(circuit) def jacobian(circuit: QuantumTape): @@ -117,7 +72,6 @@ def simulate_and_jacobian(circuit: QuantumTape): return np.array(0.0), np.array(0.0) ->>>>>>> 2a8cd8da (merge conflicts) _operations = frozenset( { "Identity", @@ -233,49 +187,15 @@ def accepted_observables(obs: qml.operation.Operator) -> bool: return obs.name in _observables -def accepted_analytic_measurements(m: qml.measurements.MeasurementProcess) -> bool: - """Whether or not a state based measurement is supported by ``lightning.qubit``.""" - return isinstance(m, (qml.measurements.ExpectationMP)) - - @simulator_tracking -@convert_single_circuit_to_batch +@single_tape_support class LightningQubit2(Device): - """PennyLane Lightning Qubit device. - - A device that interfaces with C++ to perform fast linear algebra calculations. - - Use of this device requires pre-built binaries or compilation from source. Check out the - :doc:`/lightning_qubit/installation` guide for more details. - - Args: - wires (int): the number of wires to initialize the device with - c_dtype: Datatypes for statevector representation. Must be one of - ``np.complex64`` or ``np.complex128``. - shots (int): How many times the circuit should be evaluated (or sampled) to estimate - the expectation values. Defaults to ``None`` if not specified. Setting - to ``None`` results in computing statistics like expectation values and - variances analytically. - seed (str, int, rng) - mcmc (bool): Determine whether to use the approximate Markov Chain Monte Carlo - sampling method when generating samples. - kernel_name (str): name of transition kernel. The current version supports - two kernels: ``"Local"`` and ``"NonZeroRandom"``. - The local kernel conducts a bit-flip local transition between states. - The local kernel generates a random qubit site and then generates a random - number to determine the new bit at that qubit site. The ``"NonZeroRandom"`` kernel - randomly transits between states that have nonzero probability. - num_burnin (int): number of steps that will be dropped. Increasing this value will - result in a closer approximation but increased runtime. - batch_obs (bool): Determine whether we process observables in parallel when - computing the jacobian. This value is only relevant when the lightning - qubit is built with OpenMP. - """ - - name = "lightning.qubit2" + """PennyLane Lightning Qubit device.""" _device_options = ("rng", "c_dtype", "batch_obs", "mcmc", "kernel_name", "num_burnin") + _CPP_BINARY_AVAILABLE = LQ_CPP_BINARY_AVAILABLE + def __init__( # pylint: disable=too-many-arguments self, wires, @@ -289,9 +209,12 @@ def __init__( # pylint: disable=too-many-arguments batch_obs=False, ): if not LQ_CPP_BINARY_AVAILABLE: - raise ImportError("Pre-compiled binaries for lightning.qubit are not available. " + raise ImportError( + "Pre-compiled binaries for lightning.qubit are not available. " "To manually compile from source, follow the instructions at " - "https://pennylane-lightning.readthedocs.io/en/latest/installation.html.") + "https://pennylane-lightning.readthedocs.io/en/latest/installation.html." + ) + super().__init__(wires=wires, shots=shots) self._statevector = LightningStateVector(num_wires=len(self.wires), dtype=c_dtype) @@ -370,9 +293,7 @@ def supports_derivatives( def preprocess(self, execution_config: ExecutionConfig = DefaultExecutionConfig): program = TransformProgram() - program.add_transform( - validate_measurements, analytic_measurements=accepted_analytic_measurements, name=self.name - ) + program.add_transform(validate_measurements, name=self.name) program.add_transform(no_sampling) program.add_transform(validate_observables, accepted_observables, name=self.name) program.add_transform(validate_device_wires, self.wires, name=self.name) @@ -386,7 +307,6 @@ def execute( circuits: QuantumTape_or_Batch, execution_config: ExecutionConfig = DefaultExecutionConfig, ) -> Result_or_ResultBatch: - results = [] for circuit in circuits: circuit = circuit.map_to_standard_wires() diff --git a/tests/lightning_qubit2/test_expval_2.py b/tests/lightning_qubit2/test_expval_2.py index 78553c3e44..17a1301538 100644 --- a/tests/lightning_qubit2/test_expval_2.py +++ b/tests/lightning_qubit2/test_expval_2.py @@ -34,14 +34,11 @@ VARPHI = np.linspace(0.02, 1, 3) +@pytest.mark.parametrize("c_dtype", (np.complex64, np.complex128)) @pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI))) class TestExpval: """Test expectation value calculations""" - @pytest.fixture(params=[np.complex64, np.complex128]) - def dev(self, request): - return LightningQubit(c_dtype=request.param) - @staticmethod def calculate_reference(tape): dev = DefaultQubit(max_workers=1) @@ -57,53 +54,52 @@ def process_and_execute(dev, tape): results = dev.execute(tapes) return transf_fn(results) - def test_Identity(self, theta, phi, dev, tol): + def test_Identity(self, theta, phi, c_dtype, tol): """Tests applying identities.""" - with qml.tape.QuantumTape() as tape: - qml.Identity(wires=[0]) - qml.Identity(wires=[0, 1]) - qml.Identity(wires=[1, 2]) - qml.RX(theta, wires=[0]) - qml.RX(phi, wires=[1]) - qml.expval(qml.PauliX(0)) - - calculated_val = self.process_and_execute(dev, tape) - reference_val = self.calculate_reference(tape) - + ops = [ + qml.Identity(0), + qml.Identity((0,1)), + qml.Identity((1,2)), + qml.RX(theta, 0), + qml.RX(phi, 1) + ] + measurements = [qml.expval(qml.PauliZ(0))] + tape = qml.tape.QuantumScript(ops, measurements) + + dev = LightningQubit(c_dtype=c_dtype, wires=3) + result = dev.execute(tape) + expected = np.cos(theta) tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 - assert np.allclose(calculated_val, reference_val, tol) + assert np.allclose(result, expected, tol) - def test_identity_expectation(self, theta, phi, dev, tol): - """Tests identity.""" + def test_identity_expectation(self, theta, phi, c_dtype, tol): + """Tests identity expectations.""" + dev = LightningQubit(wires=2, c_dtype=c_dtype) tape = qml.tape.QuantumScript( [qml.RX(theta, wires=[0]), qml.RX(phi, wires=[1]), qml.CNOT(wires=[0, 1])], [qml.expval(qml.Identity(wires=[0])), qml.expval(qml.Identity(wires=[1]))], ) - - calculated_val = self.process_and_execute(dev, tape) - reference_val = self.calculate_reference(tape) + result = dev.execute(tape) tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 - assert np.allclose(calculated_val, reference_val, tol) + assert np.allclose(1.0, result, tol) - def test_multi_wire_identity_expectation(self, theta, phi, dev, tol): + def test_multi_wire_identity_expectation(self, theta, phi, c_dtype, tol): """Tests multi-wire identity.""" + dev = LightningQubit(wires=2, c_dtype=c_dtype) tape = qml.tape.QuantumScript( [qml.RX(theta, wires=[0]), qml.RX(phi, wires=[1]), qml.CNOT(wires=[0, 1])], [qml.expval(qml.Identity(wires=[0, 1]))], ) - - calculated_val = self.process_and_execute(dev, tape) - reference_val = self.calculate_reference(tape) - + result = dev.execute(tape) tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 - assert np.allclose(calculated_val, reference_val, tol) + assert np.allclose(1.0, result, tol) @pytest.mark.parametrize( "wires", @@ -113,9 +109,10 @@ def test_multi_wire_identity_expectation(self, theta, phi, dev, tol): (["b", "a"]), ], ) - def test_PauliZ_expectation(self, theta, phi, dev, tol, wires): + def test_PauliZ_expectation(self, theta, phi, c_dtype, tol, wires): """Tests PauliZ.""" + dev = LightningQubit(wires=wires, c_dtype=c_dtype) tape = qml.tape.QuantumScript( [qml.RX(theta, wires=wires[0]), qml.RX(phi, wires=wires[1]), qml.CNOT(wires=wires)], [qml.expval(qml.PauliZ(wires=wires[0])), qml.expval(qml.PauliZ(wires=wires[1]))], From 07041bcce09c20a065bbf63cf8809f9c9592fda5 Mon Sep 17 00:00:00 2001 From: albi3ro Date: Wed, 21 Feb 2024 15:36:39 -0500 Subject: [PATCH 126/428] register with setup.py, state vector fixes --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 8c6fbfdf68..df732bdf87 100644 --- a/setup.py +++ b/setup.py @@ -186,6 +186,7 @@ def build_extension(self, ext: CMakeExtension): suffix = suffix[0].upper() + suffix[1:] pennylane_plugins = [device_name + " = pennylane_lightning." + backend + ":Lightning" + suffix] +pennylane_plugins.append("lightning.qubit2 = pennylane_lightning.lightning_qubit.lightning_qubit2:LightningQubit2") pkg_suffix = "" if suffix == "Qubit" else "_" + suffix From 7229bbf86f9fc0e75027641d8047fa82eb316676 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Fri, 23 Feb 2024 13:49:11 -0500 Subject: [PATCH 127/428] add LightningQubit2 to init and format --- pennylane_lightning/lightning_qubit/__init__.py | 1 + tests/lightning_qubit2/test_expval_2.py | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/__init__.py b/pennylane_lightning/lightning_qubit/__init__.py index a1b792afde..017f7fedec 100644 --- a/pennylane_lightning/lightning_qubit/__init__.py +++ b/pennylane_lightning/lightning_qubit/__init__.py @@ -16,3 +16,4 @@ from pennylane_lightning.core import __version__ from .lightning_qubit import LightningQubit +from .lightning_qubit2 import LightningQubit2 diff --git a/tests/lightning_qubit2/test_expval_2.py b/tests/lightning_qubit2/test_expval_2.py index 17a1301538..1bf57af8a1 100644 --- a/tests/lightning_qubit2/test_expval_2.py +++ b/tests/lightning_qubit2/test_expval_2.py @@ -59,10 +59,10 @@ def test_Identity(self, theta, phi, c_dtype, tol): ops = [ qml.Identity(0), - qml.Identity((0,1)), - qml.Identity((1,2)), + qml.Identity((0, 1)), + qml.Identity((1, 2)), qml.RX(theta, 0), - qml.RX(phi, 1) + qml.RX(phi, 1), ] measurements = [qml.expval(qml.PauliZ(0))] tape = qml.tape.QuantumScript(ops, measurements) From 3cca273e68cdf39f8587b84ecfad90fb928e2651 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Wed, 28 Feb 2024 13:53:38 -0500 Subject: [PATCH 128/428] add cpp binary available variable --- pennylane_lightning/lightning_qubit/lightning_qubit2.py | 5 ++++- tests/lightning_qubit2/test_expval_2.py | 6 +++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index 2153a201ae..eb16d41583 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -208,7 +208,10 @@ def __init__( # pylint: disable=too-many-arguments num_burnin=100, batch_obs=False, ): - if not LQ_CPP_BINARY_AVAILABLE: + if LQ_CPP_BINARY_AVAILABLE: + self._CPP_BINARY_AVAILABLE = True + else: + self._CPP_BINARY_AVAILABLE = False raise ImportError( "Pre-compiled binaries for lightning.qubit are not available. " "To manually compile from source, follow the instructions at " diff --git a/tests/lightning_qubit2/test_expval_2.py b/tests/lightning_qubit2/test_expval_2.py index 1bf57af8a1..abdaf05a6a 100644 --- a/tests/lightning_qubit2/test_expval_2.py +++ b/tests/lightning_qubit2/test_expval_2.py @@ -23,12 +23,12 @@ from conftest import LightningDevice # tested device -if not LightningQubit._CPP_BINARY_AVAILABLE: - pytest.skip("No binary module found. Skipping.", allow_module_level=True) - if LightningDevice != LightningQubit: pytest.skip("Exclusive tests for lightning.qubit. Skipping.", allow_module_level=True) +if not LightningQubit._CPP_BINARY_AVAILABLE: + pytest.skip("No binary module found. Skipping.", allow_module_level=True) + THETA = np.linspace(0.11, 1, 3) PHI = np.linspace(0.32, 1, 3) VARPHI = np.linspace(0.02, 1, 3) From be6fb51e0ae401ff8d58e245cbdac41ab6b558bf Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Thu, 29 Feb 2024 08:29:56 -0500 Subject: [PATCH 129/428] reduce dependency on DefaultQubit for tests --- tests/lightning_qubit2/test_expval_2.py | 120 +++++++++++------------- 1 file changed, 56 insertions(+), 64 deletions(-) diff --git a/tests/lightning_qubit2/test_expval_2.py b/tests/lightning_qubit2/test_expval_2.py index abdaf05a6a..1349e02b94 100644 --- a/tests/lightning_qubit2/test_expval_2.py +++ b/tests/lightning_qubit2/test_expval_2.py @@ -18,7 +18,7 @@ import numpy as np import pennylane as qml -from pennylane_lightning.lightning_qubit import LightningQubit2 as LightningQubit +from pennylane_lightning.lightning_qubit import LightningQubit, LightningQubit2 from pennylane.devices import DefaultQubit from conftest import LightningDevice # tested device @@ -26,7 +26,7 @@ if LightningDevice != LightningQubit: pytest.skip("Exclusive tests for lightning.qubit. Skipping.", allow_module_level=True) -if not LightningQubit._CPP_BINARY_AVAILABLE: +if not LightningQubit2._CPP_BINARY_AVAILABLE: pytest.skip("No binary module found. Skipping.", allow_module_level=True) THETA = np.linspace(0.11, 1, 3) @@ -34,11 +34,14 @@ VARPHI = np.linspace(0.02, 1, 3) -@pytest.mark.parametrize("c_dtype", (np.complex64, np.complex128)) @pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI))) class TestExpval: """Test expectation value calculations""" + @pytest.fixture(params=[np.complex64, np.complex128]) + def dev(self, request): + return LightningQubit2(wires=3, c_dtype=request.param) + @staticmethod def calculate_reference(tape): dev = DefaultQubit(max_workers=1) @@ -54,7 +57,7 @@ def process_and_execute(dev, tape): results = dev.execute(tapes) return transf_fn(results) - def test_Identity(self, theta, phi, c_dtype, tol): + def test_Identity(self, theta, phi, dev, tol): """Tests applying identities.""" ops = [ @@ -67,17 +70,15 @@ def test_Identity(self, theta, phi, c_dtype, tol): measurements = [qml.expval(qml.PauliZ(0))] tape = qml.tape.QuantumScript(ops, measurements) - dev = LightningQubit(c_dtype=c_dtype, wires=3) result = dev.execute(tape) expected = np.cos(theta) tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 assert np.allclose(result, expected, tol) - def test_identity_expectation(self, theta, phi, c_dtype, tol): + def test_identity_expectation(self, theta, phi, dev, tol): """Tests identity expectations.""" - dev = LightningQubit(wires=2, c_dtype=c_dtype) tape = qml.tape.QuantumScript( [qml.RX(theta, wires=[0]), qml.RX(phi, wires=[1]), qml.CNOT(wires=[0, 1])], [qml.expval(qml.Identity(wires=[0])), qml.expval(qml.Identity(wires=[1]))], @@ -88,10 +89,9 @@ def test_identity_expectation(self, theta, phi, c_dtype, tol): assert np.allclose(1.0, result, tol) - def test_multi_wire_identity_expectation(self, theta, phi, c_dtype, tol): + def test_multi_wire_identity_expectation(self, theta, phi, dev, tol): """Tests multi-wire identity.""" - dev = LightningQubit(wires=2, c_dtype=c_dtype) tape = qml.tape.QuantumScript( [qml.RX(theta, wires=[0]), qml.RX(phi, wires=[1]), qml.CNOT(wires=[0, 1])], [qml.expval(qml.Identity(wires=[0, 1]))], @@ -109,68 +109,66 @@ def test_multi_wire_identity_expectation(self, theta, phi, c_dtype, tol): (["b", "a"]), ], ) - def test_PauliZ_expectation(self, theta, phi, c_dtype, tol, wires): - """Tests PauliZ.""" + def test_custom_wires(self, theta, phi, tol, wires): + """Tests custom wires.""" + dev = LightningQubit2(wires=wires) - dev = LightningQubit(wires=wires, c_dtype=c_dtype) tape = qml.tape.QuantumScript( [qml.RX(theta, wires=wires[0]), qml.RX(phi, wires=wires[1]), qml.CNOT(wires=wires)], [qml.expval(qml.PauliZ(wires=wires[0])), qml.expval(qml.PauliZ(wires=wires[1]))], ) calculated_val = self.process_and_execute(dev, tape) - reference_val = self.calculate_reference(tape) - - tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 - - assert np.allclose(calculated_val, reference_val, tol) - - def test_PauliX_expectation(self, theta, phi, dev, tol): - """Tests PauliX.""" - - tape = qml.tape.QuantumScript( - [qml.RY(theta, wires=[0]), qml.RY(phi, wires=[1]), qml.CNOT(wires=[0, 1])], - [qml.expval(qml.PauliX(wires=[0])), qml.expval(qml.PauliX(wires=[1]))], - ) - - calculated_val = self.process_and_execute(dev, tape) - reference_val = self.calculate_reference(tape) - - tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 - - assert np.allclose(calculated_val, reference_val, tol) - - def test_PauliY_expectation(self, theta, phi, dev, tol): - """Tests PauliY.""" - - tape = qml.tape.QuantumScript( - [qml.RX(theta, wires=[0]), qml.RX(phi, wires=[1]), qml.CNOT(wires=[0, 1])], - [qml.expval(qml.PauliY(wires=[0])), qml.expval(qml.PauliY(wires=[1]))], - ) - - calculated_val = self.process_and_execute(dev, tape) - reference_val = self.calculate_reference(tape) + reference_val = np.array([np.cos(theta), np.cos(theta) * np.cos(phi)]) tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 assert np.allclose(calculated_val, reference_val, tol) - def test_hadamard_expectation(self, theta, phi, dev, tol): - """Tests Hadamard.""" + @pytest.mark.parametrize( + "Obs, Op, expected_fn", + [ + ( + [qml.PauliX(wires=[0]), qml.PauliX(wires=[1])], + qml.RY, + lambda theta, phi: np.array([np.sin(theta) * np.sin(phi), np.sin(phi)]), + ), + ( + [qml.PauliY(wires=[0]), qml.PauliY(wires=[1])], + qml.RX, + lambda theta, phi: np.array([0, -np.cos(theta) * np.sin(phi)]), + ), + ( + [qml.PauliZ(wires=[0]), qml.PauliZ(wires=[1])], + qml.RX, + lambda theta, phi: np.array([np.cos(theta), np.cos(theta) * np.cos(phi)]), + ), + ( + [qml.Hadamard(wires=[0]), qml.Hadamard(wires=[1])], + qml.RY, + lambda theta, phi: np.array( + [ + np.sin(theta) * np.sin(phi) + np.cos(theta), + np.cos(theta) * np.cos(phi) + np.sin(phi), + ] + ) + / np.sqrt(2), + ), + ], + ) + def test_single_wire_observables_expectation(self, Obs, Op, expected_fn, theta, phi, tol, dev): + """Test that expectation values for single wire observables are correct""" tape = qml.tape.QuantumScript( - [qml.RY(theta, wires=[0]), qml.RY(phi, wires=[1]), qml.CNOT(wires=[0, 1])], - [qml.expval(qml.Hadamard(wires=[0])), qml.expval(qml.Hadamard(wires=[1]))], + [Op(theta, wires=[0]), Op(phi, wires=[1]), qml.CNOT(wires=[0, 1])], + [qml.expval(Obs[0]), qml.expval(Obs[1])], ) + result = self.process_and_execute(dev, tape) + expected = expected_fn(theta, phi) - calculated_val = self.process_and_execute(dev, tape) - reference_val = self.calculate_reference(tape) - - tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 - - assert np.allclose(calculated_val, reference_val, tol) + assert np.allclose(result, expected, tol) - def test_hermitian_expectation(self, theta, phi, dev, tol): + def test_hermitian_expectation(self, theta, phi, tol, dev): """Tests an Hermitian operator.""" with qml.tape.QuantumTape() as tape: @@ -184,11 +182,9 @@ def test_hermitian_expectation(self, theta, phi, dev, tol): calculated_val = self.process_and_execute(dev, tape) reference_val = self.calculate_reference(tape) - tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 - assert np.allclose(calculated_val, reference_val, tol) - def test_hamiltonian_expectation(self, theta, phi, dev, tol): + def test_hamiltonian_expectation(self, theta, phi, tol, dev): """Tests a Hamiltonian.""" ham = qml.Hamiltonian( @@ -211,11 +207,9 @@ def test_hamiltonian_expectation(self, theta, phi, dev, tol): calculated_val = self.process_and_execute(dev, tape) reference_val = self.calculate_reference(tape) - tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 - assert np.allclose(calculated_val, reference_val, tol) - def test_sparse_hamiltonian_expectation(self, theta, phi, dev, tol): + def test_sparse_hamiltonian_expectation(self, theta, phi, tol, dev): """Tests a Hamiltonian.""" ham = qml.SparseHamiltonian( @@ -240,8 +234,6 @@ def test_sparse_hamiltonian_expectation(self, theta, phi, dev, tol): calculated_val = self.process_and_execute(dev, tape) reference_val = self.calculate_reference(tape) - tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 - assert np.allclose(calculated_val, reference_val, tol) @@ -251,7 +243,7 @@ class TestOperatorArithmetic: @pytest.fixture(params=[np.complex64, np.complex128]) def dev(self, request): - return LightningQubit(c_dtype=request.param) + return LightningQubit2(wires=2, c_dtype=request.param) @staticmethod def calculate_reference(tape): @@ -336,7 +328,7 @@ class TestTensorExpval: @pytest.fixture(params=[np.complex64, np.complex128]) def dev(self, request): - return LightningQubit(c_dtype=request.param) + return LightningQubit2(wires=3, c_dtype=request.param) @staticmethod def calculate_reference(tape): From d6fdb796b629b931a7177dd0dbb59a0f1c535eef Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Thu, 29 Feb 2024 08:31:43 -0500 Subject: [PATCH 130/428] update LightningQubit2 --- pennylane_lightning/lightning_qubit/lightning_qubit2.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index eb16d41583..2153a201ae 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -208,10 +208,7 @@ def __init__( # pylint: disable=too-many-arguments num_burnin=100, batch_obs=False, ): - if LQ_CPP_BINARY_AVAILABLE: - self._CPP_BINARY_AVAILABLE = True - else: - self._CPP_BINARY_AVAILABLE = False + if not LQ_CPP_BINARY_AVAILABLE: raise ImportError( "Pre-compiled binaries for lightning.qubit are not available. " "To manually compile from source, follow the instructions at " From 1d45164cfbddd7191bebcd94c49879476ae047ec Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Wed, 6 Mar 2024 10:39:01 -0500 Subject: [PATCH 131/428] Fixing rebase artifacts --- setup.py | 7 +- tests/conftest.py | 15 +- tests/lightning_qubit2/test_serialize_2.py | 563 --------------------- 3 files changed, 8 insertions(+), 577 deletions(-) delete mode 100644 tests/lightning_qubit2/test_serialize_2.py diff --git a/setup.py b/setup.py index df732bdf87..977f753559 100644 --- a/setup.py +++ b/setup.py @@ -186,7 +186,6 @@ def build_extension(self, ext: CMakeExtension): suffix = suffix[0].upper() + suffix[1:] pennylane_plugins = [device_name + " = pennylane_lightning." + backend + ":Lightning" + suffix] -pennylane_plugins.append("lightning.qubit2 = pennylane_lightning.lightning_qubit.lightning_qubit2:LightningQubit2") pkg_suffix = "" if suffix == "Qubit" else "_" + suffix @@ -204,9 +203,9 @@ def build_extension(self, ext: CMakeExtension): "long_description": open("README.rst").read(), "long_description_content_type": "text/x-rst", "install_requires": requirements, - "ext_modules": [] - if os.environ.get("SKIP_COMPILATION", False) - else [CMakeExtension(f"{backend}_ops")], + "ext_modules": ( + [] if os.environ.get("SKIP_COMPILATION", False) else [CMakeExtension(f"{backend}_ops")] + ), "cmdclass": {"build_ext": CMakeBuild}, "ext_package": "pennylane_lightning", "extras_require": { diff --git a/tests/conftest.py b/tests/conftest.py index dd1fecc795..96531bf421 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -87,7 +87,7 @@ def n_subsystems(request): # Looking for the device for testing. default_device = "lightning.qubit" -supported_devices = {"lightning.kokkos", "lightning.qubit", "lightning.qubit2", "lightning.gpu"} +supported_devices = {"lightning.kokkos", "lightning.qubit", "lightning.gpu"} supported_devices.update({sb.replace(".", "_") for sb in supported_devices}) @@ -113,10 +113,10 @@ def get_device(): device_name = get_device() -# if device_name not in qml.plugin_devices: -# raise qml.DeviceError( -# f"Device {device_name} does not exist. Make sure the required plugin is installed." -# ) +if device_name not in qml.plugin_devices: + raise qml.DeviceError( + f"Device {device_name} does not exist. Make sure the required plugin is installed." + ) # Device specification import pennylane_lightning.lightning_qubit as lightning_ops # Any definition of lightning_ops will do @@ -131,11 +131,6 @@ def get_device(): if hasattr(pennylane_lightning, "lightning_gpu_ops"): import pennylane_lightning.lightning_gpu_ops as lightning_ops -elif device_name == "lightning.qubit2": - from pennylane_lightning.lightning_qubit import LightningQubit2 as LightningDevice - - if hasattr(pennylane_lightning, "lightning_qubit_ops"): - import pennylane_lightning.lightning_qubit_ops as lightning_ops else: from pennylane_lightning.lightning_qubit import LightningQubit as LightningDevice diff --git a/tests/lightning_qubit2/test_serialize_2.py b/tests/lightning_qubit2/test_serialize_2.py deleted file mode 100644 index 7f14422c38..0000000000 --- a/tests/lightning_qubit2/test_serialize_2.py +++ /dev/null @@ -1,563 +0,0 @@ -# Copyright 2018-2024 Xanadu Quantum Technologies Inc. - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -""" -Unit tests for the serialization helper functions. -""" -import pytest -from conftest import device_name, LightningDevice - -import numpy as np -import pennylane as qml -from pennylane_lightning.lightning_qubit._serialize import QuantumScriptSerializer - -# from pennylane_lightning.lightning_qubit._serialize_old import QuantumScriptSerializer - -if not LightningDevice._CPP_BINARY_AVAILABLE: - pytest.skip("No binary module found. Skipping.", allow_module_level=True) - -if device_name == "lightning.kokkos": - from pennylane_lightning.lightning_kokkos_ops.observables import ( - NamedObsC64, - NamedObsC128, - HermitianObsC64, - HermitianObsC128, - TensorProdObsC64, - TensorProdObsC128, - HamiltonianC64, - HamiltonianC128, - SparseHamiltonianC64, - SparseHamiltonianC128, - ) -elif device_name == "lightning.gpu": - from pennylane_lightning.lightning_gpu_ops.observables import ( - NamedObsC64, - NamedObsC128, - HermitianObsC64, - HermitianObsC128, - TensorProdObsC64, - TensorProdObsC128, - HamiltonianC64, - HamiltonianC128, - SparseHamiltonianC64, - SparseHamiltonianC128, - ) -else: - from pennylane_lightning.lightning_qubit_ops.observables import ( - NamedObsC64, - NamedObsC128, - HermitianObsC64, - HermitianObsC128, - TensorProdObsC64, - TensorProdObsC128, - HamiltonianC64, - HamiltonianC128, - SparseHamiltonianC64, - SparseHamiltonianC128, - ) - - -def test_wrong_device_name(): - """Test the device name is not a valid option""" - - with pytest.raises(qml.DeviceError, match="The device name"): - QuantumScriptSerializer("thunder.qubit") - - -@pytest.mark.parametrize( - "obs,obs_type", - [ - (qml.PauliZ(0), NamedObsC128), - (qml.PauliZ(0) @ qml.PauliZ(1), TensorProdObsC128), - (qml.Hadamard(0), NamedObsC128), - (qml.Hermitian(np.eye(2), wires=0), HermitianObsC128), - ( - qml.PauliZ(0) @ qml.Hadamard(1) @ (0.1 * (qml.PauliZ(2) + qml.PauliX(3))), - HamiltonianC128, - ), - ( - ( - qml.Hermitian(np.eye(2), wires=0) - @ qml.Hermitian(np.eye(2), wires=1) - @ qml.Projector([0], wires=2) - ), - TensorProdObsC128, - ), - ( - qml.PauliZ(0) @ qml.Hermitian(np.eye(2), wires=1) @ qml.Projector([0], wires=2), - TensorProdObsC128, - ), - (qml.Projector([0], wires=0), HermitianObsC128), - (qml.Hamiltonian([1], [qml.PauliZ(0)]), HamiltonianC128), - (qml.sum(qml.Hadamard(0), qml.PauliX(1)), HermitianObsC128), - ( - qml.SparseHamiltonian(qml.Hamiltonian([1], [qml.PauliZ(0)]).sparse_matrix(), wires=[0]), - SparseHamiltonianC128, - ), - ], -) -def test_obs_returns_expected_type(obs, obs_type): - """Tests that observables get serialized to the expected type.""" - assert isinstance(QuantumScriptSerializer(device_name)._ob(obs), obs_type) - - -class TestSerializeObs: - """Tests for the serialize_observables function""" - - @pytest.mark.parametrize("use_csingle", [True, False]) - def test_tensor_non_tensor_return(self, use_csingle): - """Test expected serialization for a mixture of tensor product and non-tensor product - return""" - with qml.tape.QuantumTape() as tape: - qml.expval(qml.PauliZ(0) @ qml.PauliX(1)) - qml.expval(qml.Hadamard(1)) - - tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 - named_obs = NamedObsC64 if use_csingle else NamedObsC128 - - s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) - - s_expected = [ - tensor_prod_obs([named_obs("PauliZ", [0]), named_obs("PauliX", [1])]), - named_obs("Hadamard", [1]), - ] - - assert s == s_expected - - @pytest.mark.parametrize("use_csingle", [True, False]) - def test_hermitian_return(self, use_csingle): - """Test expected serialization for a Hermitian return""" - with qml.tape.QuantumTape() as tape: - qml.expval(qml.Hermitian(np.eye(4), wires=[0, 1])) - - hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 - c_dtype = np.complex64 if use_csingle else np.complex128 - - s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) - s_expected = hermitian_obs( - np.array( - [1.0, 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, 1.0], - dtype=c_dtype, - ), - [0, 1], - ) - assert s[0] == s_expected - - @pytest.mark.parametrize("use_csingle", [True, False]) - def test_hermitian_tensor_return(self, use_csingle): - """Test expected serialization for a Hermitian return""" - with qml.tape.QuantumTape() as tape: - qml.expval(qml.Hermitian(np.eye(4), wires=[0, 1]) @ qml.Hermitian(np.eye(2), wires=[2])) - - c_dtype = np.complex64 if use_csingle else np.complex128 - tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 - hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 - s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) - - s_expected = tensor_prod_obs( - [ - hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), - hermitian_obs(np.eye(2, dtype=c_dtype).ravel(), [2]), - ] - ) - - assert s[0] == s_expected - - @pytest.mark.parametrize("use_csingle", [True, False]) - def test_mixed_tensor_return(self, use_csingle): - """Test expected serialization for a mixture of Hermitian and Pauli return""" - with qml.tape.QuantumTape() as tape: - qml.expval(qml.Hermitian(np.eye(4), wires=[0, 1]) @ qml.PauliY(2)) - - c_dtype = np.complex64 if use_csingle else np.complex128 - tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 - hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 - named_obs = NamedObsC64 if use_csingle else NamedObsC128 - - s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) - - s_expected = tensor_prod_obs( - [hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), named_obs("PauliY", [2])] - ) - - assert s[0] == s_expected - - @pytest.mark.parametrize("use_csingle", [True, False]) - def test_hamiltonian_return(self, use_csingle): - """Test expected serialization for a Hamiltonian return""" - - ham = qml.Hamiltonian( - [0.3, 0.5, 0.4], - [ - qml.Hermitian(np.eye(4), wires=[0, 1]) @ qml.PauliY(2), - qml.PauliX(0) @ qml.PauliY(2), - qml.Hermitian(np.ones((8, 8)), wires=range(3)), - ], - ) - - with qml.tape.QuantumTape() as tape: - qml.expval(ham) - - hamiltonian_obs = HamiltonianC64 if use_csingle else HamiltonianC128 - named_obs = NamedObsC64 if use_csingle else NamedObsC128 - hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 - tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 - r_dtype = np.float32 if use_csingle else np.float64 - c_dtype = np.complex64 if use_csingle else np.complex128 - - s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) - - s_expected = hamiltonian_obs( - np.array([0.3, 0.5, 0.4], dtype=r_dtype), - [ - tensor_prod_obs( - [ - hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), - named_obs("PauliY", [2]), - ] - ), - tensor_prod_obs([named_obs("PauliX", [0]), named_obs("PauliY", [2])]), - hermitian_obs(np.ones(64, dtype=c_dtype), [0, 1, 2]), - ], - ) - - assert s[0] == s_expected - - @pytest.mark.parametrize("use_csingle", [True, False]) - def test_hamiltonian_tensor_return(self, use_csingle): - """Test expected serialization for a Hamiltonian return""" - - with qml.tape.QuantumTape() as tape: - ham = qml.Hamiltonian( - [0.3, 0.5, 0.4], - [ - qml.Hermitian(np.eye(4), wires=[0, 1]) @ qml.PauliY(2), - qml.PauliX(0) @ qml.PauliY(2), - qml.Hermitian(np.ones((8, 8)), wires=range(3)), - ], - ) - qml.expval(ham @ qml.PauliZ(3)) - - hamiltonian_obs = HamiltonianC64 if use_csingle else HamiltonianC128 - named_obs = NamedObsC64 if use_csingle else NamedObsC128 - hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 - tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 - r_dtype = np.float32 if use_csingle else np.float64 - c_dtype = np.complex64 if use_csingle else np.complex128 - - s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) - - # Expression (ham @ obs) is converted internally by Pennylane - # where obs is appended to each term of the ham - s_expected = hamiltonian_obs( - np.array([0.3, 0.5, 0.4], dtype=r_dtype), - [ - tensor_prod_obs( - [ - hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), - named_obs("PauliY", [2]), - named_obs("PauliZ", [3]), - ] - ), - tensor_prod_obs( - [named_obs("PauliX", [0]), named_obs("PauliY", [2]), named_obs("PauliZ", [3])] - ), - tensor_prod_obs( - [hermitian_obs(np.ones(64, dtype=c_dtype), [0, 1, 2]), named_obs("PauliZ", [3])] - ), - ], - ) - - assert s[0] == s_expected - - @pytest.mark.parametrize("use_csingle", [True, False]) - def test_hamiltonian_mix_return(self, use_csingle): - """Test expected serialization for a Hamiltonian return""" - - ham1 = qml.Hamiltonian( - [0.3, 0.5, 0.4], - [ - qml.Hermitian(np.eye(4), wires=[0, 1]) @ qml.PauliY(2), - qml.PauliX(0) @ qml.PauliY(2), - qml.Hermitian(np.ones((8, 8)), wires=range(3)), - ], - ) - ham2 = qml.Hamiltonian( - [0.7, 0.3], - [qml.PauliX(0) @ qml.Hermitian(np.eye(4), wires=[1, 2]), qml.PauliY(0) @ qml.PauliX(2)], - ) - - with qml.tape.QuantumTape() as tape: - qml.expval(ham1) - qml.expval(ham2) - - hamiltonian_obs = HamiltonianC64 if use_csingle else HamiltonianC128 - named_obs = NamedObsC64 if use_csingle else NamedObsC128 - hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 - tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 - r_dtype = np.float32 if use_csingle else np.float64 - c_dtype = np.complex64 if use_csingle else np.complex128 - - s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) - - s_expected1 = hamiltonian_obs( - np.array([0.3, 0.5, 0.4], dtype=r_dtype), - [ - tensor_prod_obs( - [ - hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), - named_obs("PauliY", [2]), - ] - ), - tensor_prod_obs([named_obs("PauliX", [0]), named_obs("PauliY", [2])]), - hermitian_obs(np.ones(64, dtype=c_dtype), [0, 1, 2]), - ], - ) - s_expected2 = hamiltonian_obs( - np.array([0.7, 0.3], dtype=r_dtype), - [ - tensor_prod_obs( - [ - named_obs("PauliX", [0]), - hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [1, 2]), - ] - ), - tensor_prod_obs([named_obs("PauliY", [0]), named_obs("PauliX", [2])]), - ], - ) - - assert s[0] == s_expected1 - assert s[1] == s_expected2 - - @pytest.mark.parametrize( - "obs,coeffs,terms", - [ - (qml.prod(qml.PauliZ(0), qml.PauliX(1)), [1], [[("PauliX", 1), ("PauliZ", 0)]]), - (qml.s_prod(0.1, qml.PauliX(0)), [0.1], ("PauliX", 0)), - ( - qml.sum( - 0.5 * qml.prod(qml.PauliX(0), qml.PauliZ(1)), - 0.1 * qml.prod(qml.PauliZ(0), qml.PauliY(1)), - ), - [0.5, 0.1], - [[("PauliZ", 1), ("PauliX", 0)], [("PauliY", 1), ("PauliZ", 0)]], - ), - ], - ) - @pytest.mark.parametrize("use_csingle", [True, False]) - def test_op_arithmetic_uses_hamiltonian(self, use_csingle, obs, coeffs, terms): - """Tests that an arithmetic obs with a PauliRep serializes as a Hamiltonian.""" - tape = qml.tape.QuantumTape(measurements=[qml.expval(obs)]) - res, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) - assert len(res) == 1 - assert isinstance(res[0], HamiltonianC64 if use_csingle else HamiltonianC128) - - hamiltonian_obs = HamiltonianC64 if use_csingle else HamiltonianC128 - tensor_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 - named_obs = NamedObsC64 if use_csingle else NamedObsC128 - rtype = np.float32 if use_csingle else np.float64 - term_shape = np.array(terms).shape - - if len(term_shape) == 1: # just a single pauli op - expected_terms = [named_obs(terms[0], [terms[1]])] - elif len(term_shape) == 3: # list of tensor products - expected_terms = [ - tensor_obs([named_obs(pauli, [wire]) for pauli, wire in term]) for term in terms - ] - - coeffs = np.array(coeffs).astype(rtype) - assert res[0] == hamiltonian_obs(coeffs, expected_terms) - - @pytest.mark.parametrize("use_csingle", [True, False]) - def test_multi_wire_identity(self, use_csingle): - """Tests that multi-wire Identity does not fail serialization.""" - tape = qml.tape.QuantumTape(measurements=[qml.expval(qml.Identity(wires=[1, 2]))]) - res, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) - assert len(res) == 1 - - named_obs = NamedObsC64 if use_csingle else NamedObsC128 - assert res[0] == named_obs("Identity", [1]) - - -class TestSerializeOps: - """Tests for the _ops function""" - - def test_basic_circuit(self): - """Test expected serialization for a simple circuit""" - with qml.tape.QuantumTape() as tape: - qml.RX(0.4, wires=0) - qml.RY(0.6, wires=1) - qml.CNOT(wires=[0, 1]) - - s = QuantumScriptSerializer(device_name).serialize_ops(tape) - s_expected = ( - ( - ["RX", "RY", "CNOT"], - [np.array([0.4]), np.array([0.6]), []], - [[0], [1], [0, 1]], - [False, False, False], - [[], [], []], - [[], [], []], - [[], [], []], - ), - False, - ) - assert s == s_expected - - def test_basic_circuit_not_implemented_ctrl_ops(self): - """Test expected serialization for a simple circuit""" - ops = qml.OrbitalRotation(0.1234, wires=range(4)) - with qml.tape.QuantumTape() as tape: - qml.RX(0.4, wires=0) - qml.RY(0.6, wires=1) - qml.ctrl(ops, [4, 5]) - - s = QuantumScriptSerializer(device_name).serialize_ops(tape) - s_expected = ( - ( - ["RX", "RY", "QubitUnitary"], - [np.array([0.4]), np.array([0.6]), [0.0]], - [[0], [1], list(ops.wires)], - [False, False, False], - [[], [], [qml.matrix(ops)]], - [[], [], [4, 5]], - ), - False, - ) - assert s[0][0] == s_expected[0][0] - assert s[0][1] == s_expected[0][1] - assert s[0][2] == s_expected[0][2] - assert s[0][3] == s_expected[0][3] - assert all(np.allclose(s0, s1) for s0, s1 in zip(s[0][4], s_expected[0][4])) - assert s[0][5] == s_expected[0][5] - assert s[1] == s_expected[1] - - def test_multicontrolledx(self): - """Test expected serialization for a simple circuit""" - with qml.tape.QuantumTape() as tape: - qml.RX(0.4, wires=0) - qml.RY(0.6, wires=1) - qml.ctrl(qml.PauliX(wires=0), [1, 2, 3], control_values=[True, False, False]) - - s = QuantumScriptSerializer(device_name).serialize_ops(tape) - s_expected = ( - ( - ["RX", "RY", "PauliX"], - [np.array([0.4]), np.array([0.6]), []], - [[0], [1], [0]], - [False, False, False], - [[], [], []], - [[], [], [1, 2, 3]], - [[], [], [True, False, False]], - ), - False, - ) - assert s == s_expected - - @pytest.mark.parametrize("stateprep", [qml.QubitStateVector, qml.StatePrep]) - def test_skips_prep_circuit(self, stateprep): - """Test expected serialization for a simple circuit with state preparation, such that - the state preparation is skipped""" - with qml.tape.QuantumTape() as tape: - stateprep([1, 0], wires=0) - qml.BasisState([1], wires=1) - qml.RX(0.4, wires=0) - qml.RY(0.6, wires=1) - qml.CNOT(wires=[0, 1]) - - s = QuantumScriptSerializer(device_name).serialize_ops(tape) - s_expected = ( - ( - ["RX", "RY", "CNOT"], - [[0.4], [0.6], []], - [[0], [1], [0, 1]], - [False, False, False], - [[], [], []], - [[], [], []], - [[], [], []], - ), - True, - ) - assert s == s_expected - - def test_unsupported_kernel_circuit(self): - """Test expected serialization for a circuit including gates that do not have a dedicated - kernel""" - with qml.tape.QuantumTape() as tape: - qml.CNOT(wires=[0, 1]) - qml.RZ(0.2, wires=2) - - s = QuantumScriptSerializer(device_name).serialize_ops(tape) - s_expected = ( - ( - ["CNOT", "RZ"], - [[], [0.2]], - [[0, 1], [2]], - [False, False], - ), - False, - ) - assert s[0][0] == s_expected[0][0] - assert s[0][1] == s_expected[0][1] - - @pytest.mark.parametrize("C", [True, False]) - def test_integration(self, C): - """Test expected serialization for a random circuit""" - with qml.tape.QuantumTape() as tape: - qml.RX(0.4, wires=0) - qml.RY(0.6, wires=1) - qml.CNOT(wires=[0, 1]) - qml.QubitUnitary(np.eye(4), wires=[0, 1]) - qml.templates.QFT(wires=[0, 1, 2]) - qml.DoubleExcitation(0.555, wires=[3, 2, 1, 0]) - qml.DoubleExcitationMinus(0.555, wires=[0, 1, 2, 3]) - qml.DoubleExcitationPlus(0.555, wires=[0, 1, 2, 3]) - - s = QuantumScriptSerializer(device_name).serialize_ops(tape) - - dtype = np.complex64 if C else np.complex128 - s_expected = ( - ( - [ - "RX", - "RY", - "CNOT", - "QubitUnitary", - "QFT", - "DoubleExcitation", - "DoubleExcitationMinus", - "DoubleExcitationPlus", - ], - [[0.4], [0.6], [], [0.0], [], [0.555], [0.555], [0.555]], - [[0], [1], [0, 1], [0, 1], [0, 1, 2], [3, 2, 1, 0], [0, 1, 2, 3], [0, 1, 2, 3]], - [False, False, False, False, False, False, False, False], - [ - [], - [], - [], - qml.matrix(qml.QubitUnitary(np.eye(4, dtype=dtype), wires=[0, 1])), - qml.matrix(qml.templates.QFT(wires=[0, 1, 2])), - [], - [], - [], - ], - ), - False, - ) - assert s[0][0] == s_expected[0][0] - assert s[0][1] == s_expected[0][1] - assert s[0][2] == s_expected[0][2] - assert s[0][3] == s_expected[0][3] - assert s[1] == s_expected[1] - - assert all(np.allclose(s1, s2) for s1, s2 in zip(s[0][4], s_expected[0][4])) From 57869f408bdaf698552c4ced70eb463f864b88da Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Wed, 6 Mar 2024 16:59:02 +0000 Subject: [PATCH 132/428] Add fewLQ2 tests. --- .../lightning_qubit/lightning_qubit2.py | 5 + tests/test_apply.py | 125 +++++++++++------- 2 files changed, 84 insertions(+), 46 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index 999b64c442..cce58afee0 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -264,6 +264,11 @@ def C_DTYPE(self): """State vector complex data type.""" return self._c_dtype + @property + def num_wires(self): + """State vector complex data type.""" + return self._statevector.num_wires + @classmethod @property def operations(self) -> frozenset[str]: diff --git a/tests/test_apply.py b/tests/test_apply.py index 8aae0e0553..b8caf3d97d 100644 --- a/tests/test_apply.py +++ b/tests/test_apply.py @@ -568,7 +568,6 @@ def test_expval_single_wire_no_parameters( assert np.isclose(res, expected_output, atol=tol, rtol=0) - @pytest.mark.xfail(ld._new_API, reason="Old API required") def test_expval_estimate(self): """Test that the expectation value is not analytically calculated""" dev = qml.device(device_name, wires=1, shots=3) @@ -622,7 +621,6 @@ def test_var_single_wire_no_parameters( assert np.isclose(res, expected_output, atol=tol, rtol=0) - @pytest.mark.xfail(ld._new_API, reason="Old API required") def test_var_estimate(self): """Test that the variance is not analytically calculated""" @@ -642,7 +640,6 @@ def circuit(): class TestSample: """Tests that samples are properly calculated.""" - @pytest.mark.skipif(ld._new_API, reason="Old API required") def test_sample_dimensions(self, qubit_device): """Tests if the samples returned by the sample function have the correct dimensions @@ -652,31 +649,48 @@ def test_sample_dimensions(self, qubit_device): # state is set to None in __init__ and only properly # initialized during reset dev = qubit_device(wires=2) - dev.reset() - - dev.apply([qml.RX(1.5708, wires=[0]), qml.RX(1.5708, wires=[1])]) + ops = [qml.RX(1.5708, wires=[0]), qml.RX(1.5708, wires=[1])] - dev.shots = 10 - dev._wires_measured = {0} - dev._samples = dev.generate_samples() - s1 = dev.sample(qml.PauliZ(wires=[0])) - assert np.array_equal(s1.shape, (10,)) - - dev.reset() - dev.shots = 12 - dev._wires_measured = {1} - dev._samples = dev.generate_samples() - s2 = dev.sample(qml.PauliZ(wires=[1])) - assert np.array_equal(s2.shape, (12,)) - - dev.reset() - dev.shots = 17 - dev._wires_measured = {0, 1} - dev._samples = dev.generate_samples() - s3 = dev.sample(qml.PauliX(0) @ qml.PauliZ(1)) - assert np.array_equal(s3.shape, (17,)) + shots = 10 + obs = qml.PauliZ(wires=[0]) + if ld._new_API: + tape = qml.tape.QuantumScript(ops, [qml.sample(op=obs)], shots=shots) + s1 = dev.execute(tape) + else: + dev.reset() + dev.apply(ops) + dev.shots = shots + dev._wires_measured = {0} + dev._samples = dev.generate_samples() + s1 = dev.sample(obs) + assert np.array_equal(s1.shape, (shots,)) + + shots = 12 + obs = qml.PauliZ(wires=[1]) + if ld._new_API: + tape = qml.tape.QuantumScript(ops, [qml.sample(op=obs)], shots=shots) + s2 = dev.execute(tape) + else: + dev.reset() + dev.shots = shots + dev._wires_measured = {1} + dev._samples = dev.generate_samples() + s2 = dev.sample(qml.PauliZ(wires=[1])) + assert np.array_equal(s2.shape, (shots,)) + + shots = 17 + obs = qml.PauliX(0) @ qml.PauliZ(1) + if ld._new_API: + tape = qml.tape.QuantumScript(ops, [qml.sample(op=obs)], shots=shots) + s3 = dev.execute(tape) + else: + dev.reset() + dev.shots = shots + dev._wires_measured = {0, 1} + dev._samples = dev.generate_samples() + s3 = dev.sample(qml.PauliZ(wires=[1])) + assert np.array_equal(s3.shape, (shots,)) - @pytest.mark.skipif(ld._new_API, reason="Old API required") def test_sample_values(self, qubit_device, tol): """Tests if the samples returned by sample have the correct values @@ -686,14 +700,21 @@ def test_sample_values(self, qubit_device, tol): # state is set to None in __init__ and only properly # initialized during reset dev = qubit_device(wires=2) - dev.reset() - dev.shots = 1000 - dev.apply([qml.RX(1.5708, wires=[0])]) - dev._wires_measured = {0} - dev._samples = dev.generate_samples() + ops = [qml.RX(1.5708, wires=[0])] - s1 = dev.sample(qml.PauliZ(0)) + shots = 1000 + obs = qml.PauliZ(0) + if ld._new_API: + tape = qml.tape.QuantumScript(ops, [qml.sample(op=obs)], shots=shots) + s1 = dev.execute(tape) + else: + dev.reset() + dev.apply(ops) + dev.shots = shots + dev._wires_measured = {0} + dev._samples = dev.generate_samples() + s1 = dev.sample(obs) # s1 should only contain 1 and -1, which is guaranteed if # they square to 1 @@ -704,17 +725,17 @@ class TestLightningDeviceIntegration: """Integration tests for lightning device. This test ensures it integrates properly with the PennyLane interface, in particular QNode.""" - @pytest.mark.xfail(ld._new_API, reason="Old API required") def test_load_default_qubit_device(self): """Test that the default plugin loads correctly""" dev = qml.device(device_name, wires=2) - assert dev.num_wires == 2 - assert dev.shots is None assert dev.short_name == device_name + assert dev.num_wires == 2 + if not ld._new_API: + assert dev.shots is None - @pytest.mark.xfail(ld._new_API, reason="Old API required") @pytest.mark.skipif(not ld._CPP_BINARY_AVAILABLE, reason="Lightning binary required") + @pytest.mark.xfail(ld._new_API, reason="Old API required") def test_no_backprop(self): """Test that lightning device does not support the backprop differentiation method.""" @@ -740,6 +761,8 @@ def circuit(): return qml.expval(qml.PauliZ(0)) qnode = qml.QNode(circuit, dev, diff_method="best") + print(ld) + print(qnode.device.__class__) assert isinstance(qnode.device, ld) def test_args(self): @@ -777,7 +800,6 @@ def circuit(x): assert np.isclose(circuit(p), 1, atol=tol, rtol=0) - @pytest.mark.xfail(ld._new_API, reason="Old API required") def test_nonzero_shots(self, tol_stochastic): """Test that the default qubit plugin provides correct result for high shot number""" @@ -1128,26 +1150,29 @@ def circuit(): assert np.isclose(circuit(), expected_output, atol=tol, rtol=0) - @pytest.mark.xfail(ld._new_API, reason="Old API required") def test_multi_samples_return_correlated_results(self, qubit_device): """Tests if the samples returned by the sample function have the correct dimensions """ - dev = qubit_device(wires=2) - dev.shots = 1000 + dev = qubit_device(wires=2, shots=1000) @qml.qnode(dev) def circuit(): qml.Hadamard(0) qml.CNOT(wires=[0, 1]) - return qml.sample(qml.PauliZ(0)), qml.sample(qml.PauliZ(1)) + if ld._new_API: + return qml.sample(wires=[0, 1]) + else: + return qml.sample(qml.PauliZ(0)), qml.sample(qml.PauliZ(1)) outcomes = circuit() + if ld._new_API: + outcomes = outcomes.T assert np.array_equal(outcomes[0], outcomes[1]) - @pytest.mark.parametrize("num_wires", [3, 4, 5, 6, 7, 8]) @pytest.mark.xfail(ld._new_API, reason="Old API required") + @pytest.mark.parametrize("num_wires", [3, 4, 5, 6, 7, 8]) def test_multi_samples_return_correlated_results_more_wires_than_size_of_observable( self, num_wires ): @@ -1161,13 +1186,17 @@ def test_multi_samples_return_correlated_results_more_wires_than_size_of_observa def circuit(): qml.Hadamard(0) qml.CNOT(wires=[0, 1]) - return qml.sample(qml.PauliZ(0)), qml.sample(qml.PauliZ(1)) + if ld._new_API: + return qml.sample(wires=[num_wires - 2, num_wires - 1]) + else: + return qml.sample(qml.PauliZ(0)), qml.sample(qml.PauliZ(1)) outcomes = circuit() + if ld._new_API: + outcomes = outcomes.T assert np.array_equal(outcomes[0], outcomes[1]) - @pytest.mark.xfail(ld._new_API, reason="Old API required") def test_snapshot_is_ignored_without_shot(self): """Tests if the Snapshot operator is ignored correctly""" dev = qml.device(device_name, wires=4) @@ -1184,7 +1213,6 @@ def circuit(): assert np.allclose(outcomes, [0.0]) - @pytest.mark.xfail(ld._new_API, reason="Old API required") def test_snapshot_is_ignored_with_shots(self): """Tests if the Snapshot operator is ignored correctly""" dev = qml.device(device_name, wires=4, shots=1000) @@ -1195,9 +1223,14 @@ def circuit(): qml.Snapshot() qml.adjoint(qml.Snapshot()) qml.CNOT(wires=[0, 1]) - return qml.sample(qml.PauliZ(0)), qml.sample(qml.PauliZ(1)) + if ld._new_API: + return qml.sample(wires=[0, 1]) + else: + return qml.sample(qml.PauliZ(0)), qml.sample(qml.PauliZ(1)) outcomes = circuit() + if ld._new_API: + outcomes = outcomes.T assert np.array_equal(outcomes[0], outcomes[1]) From 13130c47258e585d1c829e2eb966eaf134a1815c Mon Sep 17 00:00:00 2001 From: albi3ro Date: Wed, 6 Mar 2024 12:06:58 -0500 Subject: [PATCH 133/428] remove adjoint diff support from supports derivatives --- .../lightning_qubit/lightning_qubit2.py | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index 2153a201ae..c2b3028640 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -280,16 +280,19 @@ def supports_derivatives( execution_config: Optional[ExecutionConfig] = None, circuit: Optional[qml.tape.QuantumTape] = None, ) -> bool: - if execution_config is None and circuit is None: - return True - if execution_config.gradient_method not in {"adjoint", "best"}: - return False - if circuit is None: - return True - return ( - all(isinstance(m, qml.measurements.ExpectationMP) for m in circuit.measurements) - and not circuit.shots - ) + if False: + # to be used once adjoint differentiation support is added. + if execution_config is None and circuit is None: + return True + if execution_config.gradient_method not in {"adjoint", "best"}: + return False + if circuit is None: + return True + return ( + all(isinstance(m, qml.measurements.ExpectationMP) for m in circuit.measurements) + and not circuit.shots + ) + return False def preprocess(self, execution_config: ExecutionConfig = DefaultExecutionConfig): program = TransformProgram() From 968acff066fb9ccf64302ba40427f813a1307bfd Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Wed, 6 Mar 2024 17:54:51 +0000 Subject: [PATCH 134/428] Remove print from test_apply --- tests/test_apply.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/test_apply.py b/tests/test_apply.py index b8caf3d97d..16ec5b94ca 100644 --- a/tests/test_apply.py +++ b/tests/test_apply.py @@ -735,7 +735,6 @@ def test_load_default_qubit_device(self): assert dev.shots is None @pytest.mark.skipif(not ld._CPP_BINARY_AVAILABLE, reason="Lightning binary required") - @pytest.mark.xfail(ld._new_API, reason="Old API required") def test_no_backprop(self): """Test that lightning device does not support the backprop differentiation method.""" @@ -761,8 +760,6 @@ def circuit(): return qml.expval(qml.PauliZ(0)) qnode = qml.QNode(circuit, dev, diff_method="best") - print(ld) - print(qnode.device.__class__) assert isinstance(qnode.device, ld) def test_args(self): From 4981e8b5c98b40b58587e99152913a50dd0d6cc0 Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Wed, 6 Mar 2024 19:44:18 +0000 Subject: [PATCH 135/428] Add expval/var tests. --- tests/test_expval.py | 181 +++++++++++++++++++++++++------------------ tests/test_gates.py | 12 +-- tests/test_var.py | 88 +++++++++++---------- 3 files changed, 160 insertions(+), 121 deletions(-) diff --git a/tests/test_expval.py b/tests/test_expval.py index d1b27f9658..5f092307a0 100644 --- a/tests/test_expval.py +++ b/tests/test_expval.py @@ -19,10 +19,10 @@ import numpy as np import pennylane as qml import pytest -from conftest import PHI, THETA, VARPHI, LightningDevice, device_name +from conftest import PHI, THETA, VARPHI +from conftest import LightningDevice as ld -@pytest.mark.skipif(LightningDevice._new_API, reason="Old API required") @pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI))) class TestExpval: """Test expectation values""" @@ -33,13 +33,16 @@ def test_identity_expectation(self, theta, phi, qubit_device, tol): O1 = qml.Identity(wires=[0]) O2 = qml.Identity(wires=[1]) - - dev.apply( - [qml.RX(theta, wires=[0]), qml.RX(phi, wires=[1]), qml.CNOT(wires=[0, 1])], - rotations=[*O1.diagonalizing_gates(), *O2.diagonalizing_gates()], - ) - - res = np.array([dev.expval(O1), dev.expval(O2)]) + ops = [qml.RX(theta, wires=[0]), qml.RX(phi, wires=[1]), qml.CNOT(wires=[0, 1])] + if ld._new_API: + tape = qml.tape.QuantumScript(ops, [qml.expval(O1), qml.expval(O2)]) + res = dev.execute(tape) + else: + dev.apply( + ops, + rotations=[*O1.diagonalizing_gates(), *O2.diagonalizing_gates()], + ) + res = np.array([dev.expval(O1), dev.expval(O2)]) assert np.allclose(res, np.array([1, 1]), tol) def test_pauliz_expectation(self, theta, phi, qubit_device, tol): @@ -48,13 +51,17 @@ def test_pauliz_expectation(self, theta, phi, qubit_device, tol): O1 = qml.PauliZ(wires=[0]) O2 = qml.PauliZ(wires=[1]) - - dev.apply( - [qml.RX(theta, wires=[0]), qml.RX(phi, wires=[1]), qml.CNOT(wires=[0, 1])], - rotations=[*O1.diagonalizing_gates(), *O2.diagonalizing_gates()], - ) - - res = np.array([dev.expval(O1), dev.expval(O2)]) + ops = [qml.RX(theta, wires=[0]), qml.RX(phi, wires=[1]), qml.CNOT(wires=[0, 1])] + if ld._new_API: + tape = qml.tape.QuantumScript(ops, [qml.expval(O1), qml.expval(O2)]) + res = dev.execute(tape) + else: + dev.apply( + ops, + rotations=[*O1.diagonalizing_gates(), *O2.diagonalizing_gates()], + ) + + res = np.array([dev.expval(O1), dev.expval(O2)]) assert np.allclose(res, np.array([np.cos(theta), np.cos(theta) * np.cos(phi)]), tol) def test_paulix_expectation(self, theta, phi, qubit_device, tol): @@ -63,13 +70,18 @@ def test_paulix_expectation(self, theta, phi, qubit_device, tol): O1 = qml.PauliX(wires=[0]) O2 = qml.PauliX(wires=[1]) - - dev.apply( - [qml.RY(theta, wires=[0]), qml.RY(phi, wires=[1]), qml.CNOT(wires=[0, 1])], - rotations=[*O1.diagonalizing_gates(), *O2.diagonalizing_gates()], - ) - - res = np.array([dev.expval(O1), dev.expval(O2)], dtype=dev.C_DTYPE) + ops = [qml.RY(theta, wires=[0]), qml.RY(phi, wires=[1]), qml.CNOT(wires=[0, 1])] + if ld._new_API: + tape = qml.tape.QuantumScript(ops, [qml.expval(O1), qml.expval(O2)]) + res = dev.execute(tape) + + else: + dev.apply( + ops, + rotations=[*O1.diagonalizing_gates(), *O2.diagonalizing_gates()], + ) + + res = np.array([dev.expval(O1), dev.expval(O2)], dtype=dev.C_DTYPE) assert np.allclose( res, np.array([np.sin(theta) * np.sin(phi), np.sin(phi)], dtype=dev.C_DTYPE), tol * 10 ) @@ -80,13 +92,18 @@ def test_pauliy_expectation(self, theta, phi, qubit_device, tol): O1 = qml.PauliY(wires=[0]) O2 = qml.PauliY(wires=[1]) - - dev.apply( - [qml.RX(theta, wires=[0]), qml.RX(phi, wires=[1]), qml.CNOT(wires=[0, 1])], - rotations=[*O1.diagonalizing_gates(), *O2.diagonalizing_gates()], - ) - - res = np.array([dev.expval(O1), dev.expval(O2)]) + ops = [qml.RX(theta, wires=[0]), qml.RX(phi, wires=[1]), qml.CNOT(wires=[0, 1])] + if ld._new_API: + tape = qml.tape.QuantumScript(ops, [qml.expval(O1), qml.expval(O2)]) + res = dev.execute(tape) + + else: + dev.apply( + ops, + rotations=[*O1.diagonalizing_gates(), *O2.diagonalizing_gates()], + ) + + res = np.array([dev.expval(O1), dev.expval(O2)]) assert np.allclose(res, np.array([0, -np.cos(theta) * np.sin(phi)]), tol) def test_hadamard_expectation(self, theta, phi, qubit_device, tol): @@ -95,13 +112,18 @@ def test_hadamard_expectation(self, theta, phi, qubit_device, tol): O1 = qml.Hadamard(wires=[0]) O2 = qml.Hadamard(wires=[1]) - - dev.apply( - [qml.RY(theta, wires=[0]), qml.RY(phi, wires=[1]), qml.CNOT(wires=[0, 1])], - rotations=[*O1.diagonalizing_gates(), *O2.diagonalizing_gates()], - ) - - res = np.array([dev.expval(O1), dev.expval(O2)]) + ops = [qml.RY(theta, wires=[0]), qml.RY(phi, wires=[1]), qml.CNOT(wires=[0, 1])] + if ld._new_API: + tape = qml.tape.QuantumScript(ops, [qml.expval(O1), qml.expval(O2)]) + res = dev.execute(tape) + + else: + dev.apply( + ops, + rotations=[*O1.diagonalizing_gates(), *O2.diagonalizing_gates()], + ) + + res = np.array([dev.expval(O1), dev.expval(O2)]) expected = np.array( [np.sin(theta) * np.sin(phi) + np.cos(theta), np.cos(theta) * np.cos(phi) + np.sin(phi)] ) / np.sqrt(2) @@ -257,7 +279,6 @@ def circuit(x, y): assert qml.math.allclose(g, expected) -@pytest.mark.skipif(LightningDevice._new_API, reason="Old API required") @pytest.mark.parametrize("theta,phi,varphi", list(zip(THETA, PHI, VARPHI))) class TestTensorExpval: """Test tensor expectation values""" @@ -267,18 +288,19 @@ def test_paulix_pauliy(self, theta, phi, varphi, qubit_device, tol): correctly""" dev = qubit_device(wires=3) obs = qml.PauliX(0) @ qml.PauliY(2) - - dev.apply( - [ - qml.RX(theta, wires=[0]), - qml.RX(phi, wires=[1]), - qml.RX(varphi, wires=[2]), - qml.CNOT(wires=[0, 1]), - qml.CNOT(wires=[1, 2]), - ], - rotations=obs.diagonalizing_gates(), - ) - res = dev.expval(obs) + ops = [ + qml.RX(theta, wires=[0]), + qml.RX(phi, wires=[1]), + qml.RX(varphi, wires=[2]), + qml.CNOT(wires=[0, 1]), + qml.CNOT(wires=[1, 2]), + ] + if ld._new_API: + tape = qml.tape.QuantumScript(ops, [qml.expval(op=obs)]) + res = dev.execute(tape) + else: + dev.apply(ops, rotations=obs.diagonalizing_gates()) + res = dev.expval(obs) expected = np.sin(theta) * np.sin(phi) * np.sin(varphi) @@ -289,19 +311,23 @@ def test_pauliz_identity(self, theta, phi, varphi, qubit_device, tol): correctly""" dev = qubit_device(wires=3) obs = qml.PauliZ(0) @ qml.Identity(1) @ qml.PauliZ(2) - - dev.apply( - [ - qml.RX(theta, wires=[0]), - qml.RX(phi, wires=[1]), - qml.RX(varphi, wires=[2]), - qml.CNOT(wires=[0, 1]), - qml.CNOT(wires=[1, 2]), - ], - rotations=obs.diagonalizing_gates(), - ) - - res = dev.expval(obs) + ops = [ + qml.RX(theta, wires=[0]), + qml.RX(phi, wires=[1]), + qml.RX(varphi, wires=[2]), + qml.CNOT(wires=[0, 1]), + qml.CNOT(wires=[1, 2]), + ] + if ld._new_API: + tape = qml.tape.QuantumScript(ops, [qml.expval(op=obs)]) + res = dev.execute(tape) + else: + dev.apply( + ops, + rotations=obs.diagonalizing_gates(), + ) + + res = dev.expval(obs) expected = np.cos(varphi) * np.cos(phi) @@ -312,19 +338,22 @@ def test_pauliz_hadamard_pauliy(self, theta, phi, varphi, qubit_device, tol): works correctly""" dev = qubit_device(wires=3) obs = qml.PauliZ(0) @ qml.Hadamard(1) @ qml.PauliY(2) - - dev.apply( - [ - qml.RX(theta, wires=[0]), - qml.RX(phi, wires=[1]), - qml.RX(varphi, wires=[2]), - qml.CNOT(wires=[0, 1]), - qml.CNOT(wires=[1, 2]), - ], - rotations=obs.diagonalizing_gates(), - ) - - res = dev.expval(obs) + ops = [ + qml.RX(theta, wires=[0]), + qml.RX(phi, wires=[1]), + qml.RX(varphi, wires=[2]), + qml.CNOT(wires=[0, 1]), + qml.CNOT(wires=[1, 2]), + ] + if ld._new_API: + tape = qml.tape.QuantumScript(ops, [qml.expval(op=obs)]) + res = dev.execute(tape) + else: + dev.apply( + ops, + rotations=obs.diagonalizing_gates(), + ) + res = dev.expval(obs) expected = -(np.cos(varphi) * np.sin(phi) + np.sin(varphi) * np.cos(theta)) / np.sqrt(2) assert np.allclose(res, expected, tol) diff --git a/tests/test_gates.py b/tests/test_gates.py index 231993989d..0a75946568 100644 --- a/tests/test_gates.py +++ b/tests/test_gates.py @@ -20,7 +20,9 @@ import numpy as np import pennylane as qml import pytest -from conftest import PHI, THETA, LightningDevice, device_name +from conftest import PHI, THETA +from conftest import LightningDevice as ld +from conftest import device_name @pytest.fixture @@ -82,7 +84,7 @@ def op(op_name): return ops_list.get(op_name) -@pytest.mark.parametrize("op_name", LightningDevice.operations) +@pytest.mark.parametrize("op_name", ld.operations) def test_gate_unitary_correct(op, op_name): """Test if lightning device correctly applies gates by reconstructing the unitary matrix and comparing to the expected version""" @@ -140,7 +142,7 @@ def output(input): assert np.allclose(unitary, unitary_expected) -@pytest.mark.parametrize("op_name", LightningDevice.operations) +@pytest.mark.parametrize("op_name", ld.operations) def test_inverse_unitary_correct(op, op_name): """Test if lightning device correctly applies inverse gates by reconstructing the unitary matrix and comparing to the expected version""" @@ -244,8 +246,8 @@ def output(input): assert np.allclose(unitary, random_unitary_inv) -@pytest.mark.skipif(LightningDevice._new_API, reason="Old API required") -@pytest.mark.skipif(not LightningDevice._CPP_BINARY_AVAILABLE, reason="Lightning binary required") +@pytest.mark.skipif(ld._new_API, reason="Old API required") +@pytest.mark.skipif(not ld._CPP_BINARY_AVAILABLE, reason="Lightning binary required") @pytest.mark.parametrize( "obs,has_rotation", [ diff --git a/tests/test_var.py b/tests/test_var.py index fc73a4d748..f50dac942c 100644 --- a/tests/test_var.py +++ b/tests/test_var.py @@ -17,7 +17,8 @@ import numpy as np import pennylane as qml import pytest -from conftest import PHI, THETA, VARPHI, LightningDevice +from conftest import PHI, THETA, VARPHI +from conftest import LightningDevice as ld np.random.seed(42) @@ -26,23 +27,25 @@ class TestVar: """Tests for the variance""" - @pytest.mark.skipif(LightningDevice._new_API, reason="Old API required") def test_var(self, theta, phi, qubit_device, tol): """Tests for variance calculation""" dev = qubit_device(wires=3) # test correct variance for of a rotated state - observable = qml.PauliZ(wires=[0]) - - dev.apply( - [ - qml.RX(phi, wires=[0]), - qml.RY(theta, wires=[0]), - ], - rotations=[*observable.diagonalizing_gates()], - ) - - var = dev.var(observable) + obs = qml.PauliZ(wires=[0]) + ops = [ + qml.RX(phi, wires=[0]), + qml.RY(theta, wires=[0]), + ] + if ld._new_API: + tape = qml.tape.QuantumScript(ops, [qml.var(op=obs)]) + var = dev.execute(tape) + else: + dev.apply( + ops, + rotations=[*obs.diagonalizing_gates()], + ) + var = dev.var(obs) expected = 0.25 * (3 - np.cos(2 * theta) - 2 * np.cos(theta) ** 2 * np.cos(2 * phi)) assert np.allclose(var, expected, tol) @@ -72,7 +75,6 @@ def circuit(): assert np.allclose(circ(), circ_def(), tol) -@pytest.mark.skipif(LightningDevice._new_API, reason="Old API required") @pytest.mark.parametrize("theta, phi, varphi", list(zip(THETA, PHI, VARPHI))) class TestTensorVar: """Tests for variance of tensor observables""" @@ -81,19 +83,22 @@ def test_paulix_pauliy(self, theta, phi, varphi, qubit_device, tol): """Test that a tensor product involving PauliX and PauliY works correctly""" dev = qubit_device(wires=3) obs = qml.PauliX(0) @ qml.PauliY(2) - - dev.apply( - [ - qml.RX(theta, wires=[0]), - qml.RX(phi, wires=[1]), - qml.RX(varphi, wires=[2]), - qml.CNOT(wires=[0, 1]), - qml.CNOT(wires=[1, 2]), - ], - rotations=obs.diagonalizing_gates(), - ) - - res = dev.var(obs) + ops = [ + qml.RX(theta, wires=[0]), + qml.RX(phi, wires=[1]), + qml.RX(varphi, wires=[2]), + qml.CNOT(wires=[0, 1]), + qml.CNOT(wires=[1, 2]), + ] + if ld._new_API: + tape = qml.tape.QuantumScript(ops, [qml.var(op=obs)]) + res = dev.execute(tape) + else: + dev.apply( + ops, + rotations=obs.diagonalizing_gates(), + ) + res = dev.var(obs) expected = ( 8 * np.sin(theta) ** 2 * np.cos(2 * varphi) * np.sin(phi) ** 2 @@ -110,19 +115,22 @@ def test_pauliz_hadamard_pauliy(self, theta, phi, varphi, qubit_device, tol): """Test that a tensor product involving PauliZ and PauliY and hadamard works correctly""" dev = qubit_device(wires=3) obs = qml.PauliZ(0) @ qml.Hadamard(1) @ qml.PauliY(2) - - dev.apply( - [ - qml.RX(theta, wires=[0]), - qml.RX(phi, wires=[1]), - qml.RX(varphi, wires=[2]), - qml.CNOT(wires=[0, 1]), - qml.CNOT(wires=[1, 2]), - ], - rotations=obs.diagonalizing_gates(), - ) - - res = dev.var(obs) + ops = [ + qml.RX(theta, wires=[0]), + qml.RX(phi, wires=[1]), + qml.RX(varphi, wires=[2]), + qml.CNOT(wires=[0, 1]), + qml.CNOT(wires=[1, 2]), + ] + if ld._new_API: + tape = qml.tape.QuantumScript(ops, [qml.var(op=obs)]) + res = dev.execute(tape) + else: + dev.apply( + ops, + rotations=obs.diagonalizing_gates(), + ) + res = dev.var(obs) expected = ( 3 From 4eccac3f849b5e6aab72b1c8459e839ac6f03f4e Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Wed, 6 Mar 2024 19:56:48 +0000 Subject: [PATCH 136/428] Remove duplicate class data. --- .../lightning_qubit/lightning_qubit2.py | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index 75ff1dd1fe..10684e595a 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -264,17 +264,10 @@ def C_DTYPE(self): """State vector complex data type.""" return self._c_dtype - @classmethod @property - def operations(self) -> frozenset[str]: - """The names of the supported operations.""" - return _operations - - @classmethod - @property - def observables(self) -> frozenset[str]: - """The names of the supported observables.""" - return _observables + def num_wires(self): + """State vector complex data type.""" + return self._statevector.num_wires @property def state(self): From 11227a97ea9e01b24541dcc8885e953afd27f106 Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Wed, 6 Mar 2024 19:59:06 +0000 Subject: [PATCH 137/428] Include LQ2 in linux ests. --- .github/workflows/tests_linux.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/tests_linux.yml b/.github/workflows/tests_linux.yml index 91bd44bb85..778a9637db 100644 --- a/.github/workflows/tests_linux.yml +++ b/.github/workflows/tests_linux.yml @@ -185,6 +185,7 @@ jobs: cd main/ DEVICENAME=`echo ${{ matrix.pl_backend }} | sed "s/_/./g"` PL_DEVICE=${DEVICENAME} python -m pytest tests/ $COVERAGE_FLAGS + PL_DEVICE=${DEVICENAME}2 python -m pytest tests/ $COVERAGE_FLAGS pl-device-test --device ${DEVICENAME} --skip-ops --shots=20000 $COVERAGE_FLAGS --cov-append pl-device-test --device ${DEVICENAME} --shots=None --skip-ops $COVERAGE_FLAGS --cov-append mv .coverage .coverage-${{ github.job }}-${{ matrix.pl_backend }} From 6da12f43df98dfb6f4292b7c23447eadfc67e55e Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Wed, 6 Mar 2024 20:40:32 +0000 Subject: [PATCH 138/428] Add _group_measurements support. --- .../lightning_qubit/_measurements.py | 125 +++++++++++++----- tests/test_apply.py | 29 ++-- 2 files changed, 111 insertions(+), 43 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/_measurements.py b/pennylane_lightning/lightning_qubit/_measurements.py index 26a076c225..4c9e044a5d 100644 --- a/pennylane_lightning/lightning_qubit/_measurements.py +++ b/pennylane_lightning/lightning_qubit/_measurements.py @@ -24,20 +24,27 @@ except ImportError: pass -from typing import Callable, List +from typing import Callable, List, Sequence, Union import numpy as np import pennylane as qml +from pennylane.devices.qubit.sampling import ( + _group_measurements, + _measure_with_samples_diagonalizing_gates, +) from pennylane.measurements import ( + ClassicalShadowMP, CountsMP, ExpectationMP, MeasurementProcess, ProbabilityMP, SampleMeasurement, + ShadowExpvalMP, Shots, StateMeasurement, VarianceMP, ) +from pennylane.ops import Hamiltonian, Sum from pennylane.tape import QuantumScript from pennylane.typing import Result, TensorLike from pennylane.wires import Wires @@ -264,12 +271,9 @@ def measure_final_state(self, circuit: QuantumScript) -> Result: return tuple(self.measurement(mp) for mp in circuit.measurements) # finite-shot case - results = tuple( - self.measure_with_samples( - mp, - shots=circuit.shots, - ) - for mp in circuit.measurements + results = self.measure_with_samples( + circuit.measurements, + shots=circuit.shots, ) if len(circuit.measurements) == 1: @@ -280,9 +284,74 @@ def measure_final_state(self, circuit: QuantumScript) -> Result: return results + # pylint:disable = too-many-arguments def measure_with_samples( self, - measurementprocess: SampleMeasurement, + mps: List[Union[SampleMeasurement, ClassicalShadowMP, ShadowExpvalMP]], + shots: Shots, + ) -> List[TensorLike]: + """ + Returns the samples of the measurement process performed on the given state. + This function assumes that the user-defined wire labels in the measurement process + have already been mapped to integer wires used in the device. + + Args: + mps (List[Union[SampleMeasurement, ClassicalShadowMP, ShadowExpvalMP]]): + The sample measurements to perform + shots (Shots): The number of samples to take + + Returns: + List[TensorLike[Any]]: Sample measurement results + """ + + groups, indices = _group_measurements(mps) + + all_res = [] + for group in groups: + if isinstance(group[0], ExpectationMP) and isinstance(group[0].obs, Hamiltonian): + raise TypeError(f"ExpectationMP(Hamiltonian) cannot be computed with samples.") + # measure_fn = _measure_hamiltonian_with_samples + elif isinstance(group[0], ExpectationMP) and isinstance(group[0].obs, Sum): + raise TypeError(f"ExpectationMP(Sum) cannot be computed with samples.") + # measure_fn = _measure_sum_with_samples + elif isinstance(group[0], (ClassicalShadowMP, ShadowExpvalMP)): + raise TypeError( + f"ExpectationMP(ClassicalShadowMP, ShadowExpvalMP) cannot be computed with samples." + ) + # measure_fn = _measure_classical_shadow + else: + # measure with the usual method (rotate into the measurement basis) + all_res.extend(self._measure_with_samples_diagonalizing_gates(group, shots)) + + flat_indices = [_i for i in indices for _i in i] + + # reorder results + sorted_res = tuple( + res for _, res in sorted(list(enumerate(all_res)), key=lambda r: flat_indices[r[0]]) + ) + + # put the shot vector axis before the measurement axis + if shots.has_partitioned_shots: + sorted_res = tuple(zip(*sorted_res)) + + return sorted_res + + def _apply_diagonalizing_gates(self, mps: List[SampleMeasurement], adjoint: bool = False): + if len(mps) == 1: + diagonalizing_gates = mps[0].diagonalizing_gates() + elif all(mp.obs for mp in mps): + diagonalizing_gates = qml.pauli.diagonalize_qwc_pauli_words([mp.obs for mp in mps])[0] + else: + diagonalizing_gates = [] + + if adjoint: + diagonalizing_gates = [qml.adjoint(g, lazy=False) for g in diagonalizing_gates] + + self._qubit_state.apply_operations(diagonalizing_gates) + + def _measure_with_samples_diagonalizing_gates( + self, + mps: List[SampleMeasurement], shots: Shots, ) -> TensorLike: """ @@ -291,28 +360,28 @@ def measure_with_samples( given by the measurement process. Args: - mp (~.measurements.SampleMeasurement): The sample measurement to perform - state (np.ndarray[complex]): The state vector to sample from + mps (~.measurements.SampleMeasurement): The sample measurements to perform 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 - seed-like parameter matching that of ``seed`` for ``numpy.random.default_rng``. - If no value is provided, a default RNG will be used. - prng_key (Optional[jax.random.PRNGKey]): An optional ``jax.random.PRNGKey``. This is - the key to the JAX pseudo random number generator. Only for simulation using JAX. Returns: TensorLike[Any]: Sample measurement results """ - diagonalizing_gates = measurementprocess.diagonalizing_gates() - if diagonalizing_gates: - self._qubit_state.apply_operations(diagonalizing_gates) + # apply diagonalizing gates + self._apply_diagonalizing_gates(mps) - wires = measurementprocess.wires + total_indices = self._qubit_state.num_wires + wires = qml.wires.Wires(range(total_indices)) def _process_single_shot(samples): - res = measurementprocess.process_samples(samples, wires) - return res if isinstance(measurementprocess, CountsMP) else qml.math.squeeze(res) + processed = [] + for mp in mps: + res = mp.process_samples(samples, wires) + if not isinstance(mp, CountsMP): + res = qml.math.squeeze(res) + + processed.append(res) + + return tuple(processed) # if there is a shot vector, build a list containing results for each shot entry if shots.has_partitioned_shots: @@ -331,11 +400,7 @@ def _process_single_shot(samples): samples = qml.math.full((s, len(wires)), 0) processed_samples.append(_process_single_shot(samples)) - if diagonalizing_gates: - self._qubit_state.apply_operations( - [qml.adjoint(g) for g in reversed(diagonalizing_gates)] - ) - + self._apply_diagonalizing_gates(mps, adjoint=True) return tuple(zip(*processed_samples)) try: @@ -347,8 +412,6 @@ def _process_single_shot(samples): raise e samples = qml.math.full((shots.total_shots, len(wires)), 0) - if diagonalizing_gates: - self._qubit_state.apply_operations( - [qml.adjoint(g) for g in reversed(diagonalizing_gates)] - ) + self._apply_diagonalizing_gates(mps, adjoint=True) + return _process_single_shot(samples) diff --git a/tests/test_apply.py b/tests/test_apply.py index 16ec5b94ca..86cb7a1c2a 100644 --- a/tests/test_apply.py +++ b/tests/test_apply.py @@ -533,7 +533,6 @@ def test_apply_state_vector_lightning_handle(self, qubit_device, tol): class TestExpval: """Tests that expectation values are properly calculated or that the proper errors are raised.""" - @pytest.mark.skipif(ld._new_API, reason="Old API required") @pytest.mark.parametrize( "operation,input,expected_output", [ @@ -561,10 +560,14 @@ def test_expval_single_wire_no_parameters( """Tests that expectation values are properly calculated for single-wire observables without parameters.""" dev = qubit_device(wires=1) obs = operation(wires=[0]) - - dev.reset() - dev.apply([stateprep(np.array(input), wires=[0])], obs.diagonalizing_gates()) - res = dev.expval(obs) + ops = [stateprep(np.array(input), wires=[0])] + if ld._new_API: + tape = qml.tape.QuantumScript(ops, [qml.expval(op=obs)]) + res = dev.execute(tape) + else: + dev.reset() + dev.apply(ops, obs.diagonalizing_gates()) + res = dev.expval(obs) assert np.isclose(res, expected_output, atol=tol, rtol=0) @@ -586,7 +589,6 @@ def circuit(): class TestVar: """Tests that variances are properly calculated.""" - @pytest.mark.skipif(ld._new_API, reason="Old API required") @pytest.mark.parametrize( "operation,input,expected_output", [ @@ -614,10 +616,14 @@ def test_var_single_wire_no_parameters( """Tests that variances are properly calculated for single-wire observables without parameters.""" dev = qubit_device(wires=1) obs = operation(wires=[0]) - - dev.reset() - dev.apply([stateprep(np.array(input), wires=[0])], obs.diagonalizing_gates()) - res = dev.var(obs) + ops = [stateprep(np.array(input), wires=[0])] + if ld._new_API: + tape = qml.tape.QuantumScript(ops, [qml.var(op=obs)]) + res = dev.execute(tape) + else: + dev.reset() + dev.apply(ops, obs.diagonalizing_gates()) + res = dev.var(obs) assert np.isclose(res, expected_output, atol=tol, rtol=0) @@ -1168,7 +1174,6 @@ def circuit(): assert np.array_equal(outcomes[0], outcomes[1]) - @pytest.mark.xfail(ld._new_API, reason="Old API required") @pytest.mark.parametrize("num_wires", [3, 4, 5, 6, 7, 8]) def test_multi_samples_return_correlated_results_more_wires_than_size_of_observable( self, num_wires @@ -1184,7 +1189,7 @@ def circuit(): qml.Hadamard(0) qml.CNOT(wires=[0, 1]) if ld._new_API: - return qml.sample(wires=[num_wires - 2, num_wires - 1]) + return qml.sample(wires=[0, 1]) else: return qml.sample(qml.PauliZ(0)), qml.sample(qml.PauliZ(1)) From bc870e51c1a273c993e976ca322030013092c856 Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Wed, 6 Mar 2024 15:43:17 -0500 Subject: [PATCH 139/428] --cov-append --- .github/workflows/tests_linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests_linux.yml b/.github/workflows/tests_linux.yml index 778a9637db..b334c0acce 100644 --- a/.github/workflows/tests_linux.yml +++ b/.github/workflows/tests_linux.yml @@ -185,7 +185,7 @@ jobs: cd main/ DEVICENAME=`echo ${{ matrix.pl_backend }} | sed "s/_/./g"` PL_DEVICE=${DEVICENAME} python -m pytest tests/ $COVERAGE_FLAGS - PL_DEVICE=${DEVICENAME}2 python -m pytest tests/ $COVERAGE_FLAGS + PL_DEVICE=${DEVICENAME}2 python -m pytest tests/ $COVERAGE_FLAGS --cov-append pl-device-test --device ${DEVICENAME} --skip-ops --shots=20000 $COVERAGE_FLAGS --cov-append pl-device-test --device ${DEVICENAME} --shots=None --skip-ops $COVERAGE_FLAGS --cov-append mv .coverage .coverage-${{ github.job }}-${{ matrix.pl_backend }} From ee01c2fae3626e7eb05f043d35ce7da666516a1a Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Wed, 6 Mar 2024 21:47:57 +0000 Subject: [PATCH 140/428] Add mcmc capability + tests. --- .../lightning_qubit/_measurements.py | 33 +++++++++++--- .../lightning_qubit/lightning_qubit2.py | 11 +++-- .../test_measurements_samples_MCMC.py | 44 ++++++++++++------- tests/test_apply.py | 2 +- 4 files changed, 62 insertions(+), 28 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/_measurements.py b/pennylane_lightning/lightning_qubit/_measurements.py index 4c9e044a5d..3b69cce737 100644 --- a/pennylane_lightning/lightning_qubit/_measurements.py +++ b/pennylane_lightning/lightning_qubit/_measurements.py @@ -63,11 +63,20 @@ class LightningMeasurements: qubit_state(LightningStateVector): Lightning state-vector class containing the state vector to be measured. """ - def __init__(self, qubit_state: LightningStateVector) -> None: + def __init__( + self, + qubit_state: LightningStateVector, + mcmc: bool = False, + kernel_name: str = "Local", + num_burnin: int = 100, + ) -> None: self._qubit_state = qubit_state self._state = qubit_state.state_vector self._dtype = qubit_state.dtype self._measurement_lightning = self._measurement_dtype()(self.state) + self._mcmc = mcmc + self._kernel_name = kernel_name + self._num_burnin = num_burnin @property def qubit_state(self): @@ -391,9 +400,14 @@ def _process_single_shot(samples): # better to call sample_state just once with total_shots, then use # the shot_range keyword argument try: - samples = self._measurement_lightning.generate_samples(len(wires), s).astype( - int, copy=False - ) + if self._mcmc: + samples = self._measurement_lightning.generate_mcmc_samples( + len(wires), self._kernel_name, self._num_burnin, s + ).astype(int, copy=False) + else: + samples = self._measurement_lightning.generate_samples( + len(wires), s + ).astype(int, copy=False) except ValueError as e: if str(e) != "probabilities contain NaN": raise e @@ -404,9 +418,14 @@ def _process_single_shot(samples): return tuple(zip(*processed_samples)) try: - samples = self._measurement_lightning.generate_samples( - len(wires), shots.total_shots - ).astype(int, copy=False) + if self._mcmc: + samples = self._measurement_lightning.generate_mcmc_samples( + len(wires), self._kernel_name, self._num_burnin, shots.total_shots + ).astype(int, copy=False) + else: + samples = self._measurement_lightning.generate_samples( + len(wires), shots.total_shots + ).astype(int, copy=False) except ValueError as e: if str(e) != "probabilities contain NaN": raise e diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index 0569248c70..b80c64ff74 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -51,7 +51,7 @@ PostprocessingFn = Callable[[ResultBatch], Result_or_ResultBatch] -def simulate(circuit: QuantumScript, state: LightningStateVector) -> Result: +def simulate(circuit: QuantumScript, state: LightningStateVector, mcmc: dict = {}) -> Result: """Simulate a single quantum script. Args: @@ -66,7 +66,7 @@ def simulate(circuit: QuantumScript, state: LightningStateVector) -> Result: """ state.reset_state() final_state = state.get_final_state(circuit) - return LightningMeasurements(final_state).measure_final_state(circuit) + return LightningMeasurements(final_state, **mcmc).measure_final_state(circuit) def jacobian(circuit: QuantumTape): @@ -324,10 +324,15 @@ def execute( circuits: QuantumTape_or_Batch, execution_config: ExecutionConfig = DefaultExecutionConfig, ) -> Result_or_ResultBatch: + mcmc = { + "mcmc": self._mcmc, + "kernel_name": self._kernel_name, + "num_burnin": self._num_burnin, + } results = [] for circuit in circuits: circuit = circuit.map_to_standard_wires() - results.append(simulate(circuit, self._statevector)) + results.append(simulate(circuit, self._statevector, mcmc=mcmc)) return tuple(results) diff --git a/tests/lightning_qubit/test_measurements_samples_MCMC.py b/tests/lightning_qubit/test_measurements_samples_MCMC.py index e376294353..eadb913131 100644 --- a/tests/lightning_qubit/test_measurements_samples_MCMC.py +++ b/tests/lightning_qubit/test_measurements_samples_MCMC.py @@ -17,14 +17,16 @@ import numpy as np import pennylane as qml import pytest -from conftest import LightningDevice # tested device +from conftest import LightningDevice as ld +from conftest import device_name from pennylane_lightning.lightning_qubit import LightningQubit +from pennylane_lightning.lightning_qubit2 import LightningQubit2 if not LightningQubit._CPP_BINARY_AVAILABLE: pytest.skip("No binary module found. Skipping.", allow_module_level=True) -if LightningDevice != LightningQubit: +if device_name not in ("lightning.qubit", "lightning.qubit2"): pytest.skip("Exclusive tests for lightning.qubit. Skipping.", allow_module_level=True) @@ -33,7 +35,7 @@ class TestMCMCSample: @pytest.fixture(params=[np.complex64, np.complex128]) def dev(self, request): - return qml.device("lightning.qubit", wires=2, shots=1000, mcmc=True, c_dtype=request.param) + return qml.device(device_name, wires=2, shots=1000, mcmc=True, c_dtype=request.param) test_data_no_parameters = [ (100, [0], qml.PauliZ(wires=[0]), 100), @@ -46,12 +48,16 @@ def test_mcmc_sample_dimensions(self, dev, num_shots, measured_wires, operation, """Tests if the samples returned by sample have the correct dimensions """ - dev.apply([qml.RX(1.5708, wires=[0]), qml.RX(1.5708, wires=[1])]) - - dev.shots = num_shots - dev._wires_measured = measured_wires - dev._samples = dev.generate_samples() - s1 = dev.sample(operation) + ops = [qml.RX(1.5708, wires=[0]), qml.RX(1.5708, wires=[1])] + if ld._new_API: + tape = qml.tape.QuantumScript(ops, [qml.sample(op=operation)], shots=num_shots) + s1 = dev.execute(tape) + else: + dev.apply(ops) + dev.shots = num_shots + dev._wires_measured = measured_wires + dev._samples = dev.generate_samples() + s1 = dev.sample(operation) assert np.array_equal(s1.shape, (shape,)) @@ -61,13 +67,17 @@ def test_sample_values(self, tol, kernel): the correct values """ dev = qml.device( - "lightning.qubit", wires=2, shots=1000, mcmc=True, kernel_name=kernel, num_burnin=100 + device_name, wires=2, shots=1000, mcmc=True, kernel_name=kernel, num_burnin=100 ) - - dev.apply([qml.RX(1.5708, wires=[0])]) - dev._wires_measured = {0} - dev._samples = dev.generate_samples() - s1 = dev.sample(qml.PauliZ(0)) + ops = [qml.RX(1.5708, wires=[0])] + if ld._new_API: + tape = qml.tape.QuantumScript(ops, [qml.sample(op=qml.PauliZ(0))], shots=1000) + s1 = dev.execute(tape) + else: + dev.apply([qml.RX(1.5708, wires=[0])]) + dev._wires_measured = {0} + dev._samples = dev.generate_samples() + s1 = dev.sample(qml.PauliZ(0)) # s1 should only contain 1 and -1, which is guaranteed if # they square to 1 @@ -83,7 +93,7 @@ def test_unsupported_sample_kernels(self, tol, kernel): match=f"The {kernel} is not supported and currently only 'Local' and 'NonZeroRandom' kernels are supported.", ): dev = qml.device( - "lightning.qubit", + device_name, wires=2, shots=1000, mcmc=True, @@ -93,4 +103,4 @@ def test_unsupported_sample_kernels(self, tol, kernel): def test_wrong_num_burnin(self): with pytest.raises(ValueError, match="Shots should be greater than num_burnin."): - dev = qml.device("lightning.qubit", wires=2, shots=1000, mcmc=True, num_burnin=1000) + dev = qml.device(device_name, wires=2, shots=1000, mcmc=True, num_burnin=1000) diff --git a/tests/test_apply.py b/tests/test_apply.py index 86cb7a1c2a..21d484c3be 100644 --- a/tests/test_apply.py +++ b/tests/test_apply.py @@ -754,7 +754,7 @@ def circuit(): with pytest.raises(qml.QuantumFunctionError): qml.QNode(circuit, dev, diff_method="backprop") - @pytest.mark.xfail(ld._new_API, reason="Old API required") + @pytest.mark.xfail(ld._new_API, reason="New device API currently has the wrong module path.") @pytest.mark.skipif(not ld._CPP_BINARY_AVAILABLE, reason="Lightning binary required") def test_best_gets_lightning(self): """Test that the best differentiation method returns lightning From 2c52cf1cb877cf139a2642f2c04236dbd48ff020 Mon Sep 17 00:00:00 2001 From: Dev version update bot Date: Thu, 7 Mar 2024 14:17:19 +0000 Subject: [PATCH 141/428] Auto update version --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index b8174c108a..25f4c22d24 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.36.0-dev3" +__version__ = "0.36.0-dev4" From 357973a52bf3607203636c2337fd54edc6496a8b Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Thu, 7 Mar 2024 10:29:29 -0500 Subject: [PATCH 142/428] update dev version --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index b8174c108a..25f4c22d24 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.36.0-dev3" +__version__ = "0.36.0-dev4" From 85128b57d7296dabda7ab6f70f20756483f1d353 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Thu, 7 Mar 2024 10:29:58 -0500 Subject: [PATCH 143/428] add LightningAdjointJacobian class --- .../lightning_qubit/_adjoint_jacobian.py | 341 ++++++++++++++++++ 1 file changed, 341 insertions(+) create mode 100644 pennylane_lightning/lightning_qubit/_adjoint_jacobian.py diff --git a/pennylane_lightning/lightning_qubit/_adjoint_jacobian.py b/pennylane_lightning/lightning_qubit/_adjoint_jacobian.py new file mode 100644 index 0000000000..bb220003c0 --- /dev/null +++ b/pennylane_lightning/lightning_qubit/_adjoint_jacobian.py @@ -0,0 +1,341 @@ +# Copyright 2018-2023 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +r""" +Internal methods for adjoint Jacobian differentiation method. +""" +from os import getenv +from typing import List +from warnings import warn + +import numpy as np +import pennylane as qml +from pennylane import BasisState, Projector, QuantumFunctionError, Rot, StatePrep +from pennylane.measurements import Expectation, MeasurementProcess, State +from pennylane.operation import Operation, Tensor +from pennylane.tape import QuantumTape + +from pennylane_lightning.core._serialize import QuantumScriptSerializer +from pennylane_lightning.core.lightning_base import _chunk_iterable + +# pylint: disable=import-error, no-name-in-module, ungrouped-imports +try: + from pennylane_lightning.lightning_qubit_ops.algorithms import ( + AdjointJacobianC64, + AdjointJacobianC128, + create_ops_listC64, + create_ops_listC128, + ) +except ImportError: + pass + +from ._state_vector import LightningStateVector + + +class LightningAdjointJacobian: + """Check and execute the adjoint Jacobian differentiation method. + + Args: + qubit_state(LightningStateVector): State Vector to calculate the adjoint Jacobian with. + batch_obs(bool): If serialized tape is to be batched or not. + """ + + def __init__(self, qubit_state: LightningStateVector, batch_obs: bool = False) -> None: + self._qubit_state = qubit_state + self._state = qubit_state.state_vector + self._dtype = qubit_state.dtype + self._jacobian_lightning = ( + AdjointJacobianC64() if self._dtype == np.complex64 else AdjointJacobianC128() + ) + self._create_ops_list_lightning = ( + create_ops_listC64 if self._dtype == np.complex64 else create_ops_listC128 + ) + self._batch_obs = batch_obs + + @property + def qubit_state(self): + """Returns a handle to the LightningStateVector class.""" + return self._qubit_state + + @property + def state(self): + """Returns a handle to the Lightning internal data class.""" + return self._state + + @property + def dtype(self): + """Returns the simulation data type.""" + return self._dtype + + @staticmethod + def _check_adjdiff_supported_measurements( + measurements: List[MeasurementProcess], + ): + """Check whether given list of measurement is supported by adjoint_differentiation. + + Args: + measurements (List[MeasurementProcess]): a list of measurement processes to check. + + Returns: + Expectation or State: a common return type of measurements. + """ + if not measurements: + return None + + if len(measurements) == 1 and measurements[0].return_type is State: + return State + + # Now the return_type of measurement processes must be expectation + if any(measurement.return_type is not Expectation for measurement in measurements): + raise QuantumFunctionError( + "Adjoint differentiation method does not support expectation return type " + "mixed with other return types" + ) + + for measurement in measurements: + if isinstance(measurement.obs, Tensor): + if any(isinstance(obs, Projector) for obs in measurement.obs.non_identity_obs): + raise QuantumFunctionError( + "Adjoint differentiation method does " + "not support the Projector observable" + ) + elif isinstance(measurement.obs, Projector): + raise QuantumFunctionError( + "Adjoint differentiation method does not support the Projector observable" + ) + return Expectation + + @staticmethod + def _check_adjdiff_supported_operations(operations): + """Check Lightning adjoint differentiation method support for a tape. + + Raise ``QuantumFunctionError`` if ``tape`` contains not supported measurements, + observables, or operations by the Lightning adjoint differentiation method. + + Args: + tape (.QuantumTape): quantum tape to differentiate. + """ + for operation in operations: + if operation.num_params > 1 and not isinstance(operation, Rot): + raise QuantumFunctionError( + f"The {operation.name} operation is not supported using " + 'the "adjoint" differentiation method' + ) + + # pylint: disable=too-many-function-args, assignment-from-no-return, too-many-arguments + def _process_jacobian_tape( + self, tape: QuantumTape, use_mpi: bool = False, split_obs: bool = False + ): + use_csingle = True if self._dtype == np.complex64 else False + + obs_serialized, obs_idx_offsets = QuantumScriptSerializer( + self._qubit_state.device_name, use_csingle, use_mpi, split_obs + ).serialize_observables(tape) + + ops_serialized, use_sp = QuantumScriptSerializer( + self._qubit_state.device_name, use_csingle, use_mpi, split_obs + ).serialize_ops(tape) + + ops_serialized = self._create_ops_list_lightning(*ops_serialized) + + # We need to filter out indices in trainable_params which do not + # correspond to operators. + trainable_params = sorted(tape.trainable_params) + if len(trainable_params) == 0: + return None + + tp_shift = [] + record_tp_rows = [] + all_params = 0 + + for op_idx, trainable_param in enumerate(trainable_params): + # get op_idx-th operator among differentiable operators + operation, _, _ = tape.get_operation(op_idx) + if isinstance(operation, Operation) and not isinstance( + operation, (BasisState, StatePrep) + ): + # We now just ignore non-op or state preps + tp_shift.append(trainable_param) + record_tp_rows.append(all_params) + all_params += 1 + + if use_sp: + # When the first element of the tape is state preparation. Still, I am not sure + # whether there must be only one state preparation... + tp_shift = [i - 1 for i in tp_shift] + + return { + "state_vector": self.state, + "obs_serialized": obs_serialized, + "ops_serialized": ops_serialized, + "tp_shift": tp_shift, + "record_tp_rows": record_tp_rows, + "all_params": all_params, + "obs_idx_offsets": obs_idx_offsets, + } + + @staticmethod + def _adjoint_jacobian_processing(jac): + """ + Post-process the Jacobian matrix returned by ``adjoint_jacobian`` for + the new return type system. + """ + jac = np.squeeze(jac) + + if jac.ndim == 0: + return np.array(jac) + + if jac.ndim == 1: + return tuple(np.array(j) for j in jac) + + # must be 2-dimensional + return tuple(tuple(np.array(j_) for j_ in j) for j in jac) + + def calculate_jacobian(self, tape: QuantumTape): + """Computes and returns the Jacobian with the adjoint method.""" + if tape.shots: + warn( + "Requested adjoint differentiation to be computed with finite shots. " + "The derivative is always exact when using the adjoint " + "differentiation method.", + UserWarning, + ) + + tape_return_type = self._check_adjdiff_supported_measurements(tape.measurements) + + if not tape_return_type: # the tape does not have measurements + return np.array([], dtype=self._dtype) + + if tape_return_type is State: + raise QuantumFunctionError( + "This method does not support statevector return type. " + "Use vjp method instead for this purpose." + ) + + self._check_adjdiff_supported_operations(tape.operations) + + processed_data = self._process_jacobian_tape(tape) + + if not processed_data: # training_params is empty + return np.array([], dtype=self._dtype) + + trainable_params = processed_data["tp_shift"] + + # If requested batching over observables, chunk into OMP_NUM_THREADS sized chunks. + # This will allow use of Lightning with adjoint for large-qubit numbers AND large + # numbers of observables, enabling choice between compute time and memory use. + requested_threads = int(getenv("OMP_NUM_THREADS", "1")) + + if self._batch_obs and requested_threads > 1: + obs_partitions = _chunk_iterable(processed_data["obs_serialized"], requested_threads) + jac = [] + for obs_chunk in obs_partitions: + jac_local = self._jacobian_lightning( + processed_data["state_vector"], + obs_chunk, + processed_data["ops_serialized"], + trainable_params, + ) + jac.extend(jac_local) + else: + jac = self._jacobian_lightning( + processed_data["state_vector"], + processed_data["obs_serialized"], + processed_data["ops_serialized"], + trainable_params, + ) + jac = np.array(jac) + jac = jac.reshape(-1, len(trainable_params)) + jac_r = np.zeros((jac.shape[0], processed_data["all_params"])) + jac_r[:, processed_data["record_tp_rows"]] = jac + if hasattr(qml, "active_return"): # pragma: no cover + return self._adjoint_jacobian_processing(jac_r) if qml.active_return() else jac_r + return self._adjoint_jacobian_processing(jac_r) + + # pylint: disable=line-too-long, inconsistent-return-statements + def calculate_vjp(self, tape, grad_vec): + """Generate the processing function required to compute the vector-Jacobian products + of a tape. + + .. code-block:: python + + vjp_f = dev.vjp([qml.state()], grad_vec) + vjp = vjp_f(tape) + + computes :math:`w = (w_1,\\cdots,w_m)` where + + .. math:: + + w_k = \\langle v| \\frac{\\partial}{\\partial \\theta_k} | \\psi_{\\pmb{\\theta}} \\rangle. + + Here, :math:`m` is the total number of trainable parameters, :math:`\\pmb{\\theta}` + is the vector of trainable parameters and :math:`\\psi_{\\pmb{\\theta}}` + is the output quantum state. + + Args: + measurements (list): List of measurement processes for vector-Jacobian product. + Now it must be expectation values or a quantum state. + grad_vec (tensor_like): Gradient-output vector. Must have shape matching the output + shape of the corresponding tape, i.e. number of measurements if + the return type is expectation or :math:`2^N` if the return type is statevector + + Returns: + The processing function required to compute the vector-Jacobian products of a tape. + """ + if tape.shots is not None: + warn( + "Requested adjoint differentiation to be computed with finite shots. " + "The derivative is always exact when using the adjoint differentiation " + "method.", + UserWarning, + ) + + measurements = tape.measurements + tape_return_type = self._check_adjdiff_supported_measurements(measurements) + + if qml.math.allclose(grad_vec, 0) or tape_return_type is None: + return qml.math.convert_like(np.zeros(len(tape.trainable_params)), grad_vec) + + if tape_return_type is Expectation: + if len(grad_vec) != len(measurements): + raise ValueError( + "Number of observables in the tape must be the same as the " + "length of grad_vec in the vjp method" + ) + + if np.iscomplexobj(grad_vec): + raise ValueError( + "The vjp method only works with a real-valued grad_vec when the " + "tape is returning an expectation value" + ) + + ham = qml.Hamiltonian(grad_vec, [m.obs for m in measurements]) + + def processing_fn_expval(tape): + nonlocal ham + num_params = len(tape.trainable_params) + + if num_params == 0: + return np.array([], dtype=self.qubit_state.dtype) + + new_tape = tape.copy() + new_tape._measurements = [qml.expval(ham)] + + return self.calculate_jacobian(new_tape) + + return processing_fn_expval(tape) + + if tape_return_type is State: + raise QuantumFunctionError( + "Adjoint differentiation does not support State measurements." + ) From f95e16b97a13d383a321510ebb7ed1b17309862b Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Thu, 7 Mar 2024 10:30:42 -0500 Subject: [PATCH 144/428] add unit tests for the LightningAdjointJacobian class --- .../test_adjoint_jacobian_class.py | 702 ++++++++++++++++++ 1 file changed, 702 insertions(+) create mode 100644 tests/lightning_qubit/test_adjoint_jacobian_class.py diff --git a/tests/lightning_qubit/test_adjoint_jacobian_class.py b/tests/lightning_qubit/test_adjoint_jacobian_class.py new file mode 100644 index 0000000000..141ea7613d --- /dev/null +++ b/tests/lightning_qubit/test_adjoint_jacobian_class.py @@ -0,0 +1,702 @@ +# Copyright 2018-2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +Unit tests for the adjoint Jacobian and VJP methods. +""" +import math + +import pennylane as qml +import pytest +from conftest import LightningDevice # tested device +from pennylane import numpy as np +from pennylane.tape import QuantumScript +from scipy.stats import unitary_group + +from pennylane_lightning.lightning_qubit import LightningQubit +from pennylane_lightning.lightning_qubit._adjoint_jacobian import ( + LightningAdjointJacobian, +) +from pennylane_lightning.lightning_qubit._state_vector import LightningStateVector + +if not LightningQubit._CPP_BINARY_AVAILABLE: + pytest.skip("No binary module found. Skipping.", allow_module_level=True) + +if LightningDevice != LightningQubit: + pytest.skip("Exclusive tests for lightning.qubit. Skipping.", allow_module_level=True) + + +I, X, Y, Z = ( + np.eye(2), + qml.PauliX.compute_matrix(), + qml.PauliY.compute_matrix(), + qml.PauliZ.compute_matrix(), +) + + +# General LightningStateVector fixture, for any number of wires. +@pytest.fixture( + scope="function", + params=[np.complex64, np.complex128], +) +def lightning_sv(request): + def _statevector(num_wires): + return LightningStateVector(num_wires=num_wires, dtype=request.param) + + return _statevector + + +def Rx(theta): + r"""One-qubit rotation about the x axis. + + Args: + theta (float): rotation angle + Returns: + array: unitary 2x2 rotation matrix :math:`e^{-i \sigma_x \theta/2}` + """ + return math.cos(theta / 2) * I + 1j * math.sin(-theta / 2) * X + + +def Ry(theta): + r"""One-qubit rotation about the y axis. + + Args: + theta (float): rotation angle + Returns: + array: unitary 2x2 rotation matrix :math:`e^{-i \sigma_y \theta/2}` + """ + return math.cos(theta / 2) * I + 1j * math.sin(-theta / 2) * Y + + +def Rz(theta): + r"""One-qubit rotation about the z axis. + + Args: + theta (float): rotation angle + Returns: + array: unitary 2x2 rotation matrix :math:`e^{-i \sigma_z \theta/2}` + """ + return math.cos(theta / 2) * I + 1j * math.sin(-theta / 2) * Z + + +def test_initialization(lightning_sv): + """Tests for the initialization of the LightningAdjointJacobian class.""" + statevector = lightning_sv(num_wires=5) + aj = LightningAdjointJacobian(statevector) + + assert aj.qubit_state is statevector + assert aj.state is statevector.state_vector + assert aj.dtype == statevector.dtype + + +class TestAdjointJacobian: + """Tests for the adjoint Jacobian functionality""" + + def test_not_supported_var(self, lightning_sv): + """Test if a QuantumFunctionError is raised for a tape with measurements that are not + supported""" + + with qml.tape.QuantumTape() as tape: + qml.RX(0.1, wires=0) + qml.var(qml.PauliZ(0)) + + with pytest.raises( + qml.QuantumFunctionError, match="Adjoint differentiation method does not" + ): + self.calculate_jacobian(lightning_sv(num_wires=3), tape) + + def test_not_supported_state(self, lightning_sv): + """Test if a QuantumFunctionError is raised for a tape with measurements that are not + supported""" + + with qml.tape.QuantumTape() as tape: + qml.RX(0.1, wires=0) + qml.state() + + with pytest.raises( + qml.QuantumFunctionError, + match="This method does not support statevector return type", + ): + self.calculate_jacobian(lightning_sv(num_wires=3), tape) + + def test_finite_shots_warns(self, lightning_sv): + """Tests warning raised when finite shots specified""" + tape = qml.tape.QuantumTape(measurements=[qml.expval(qml.PauliZ(0))], shots=1) + + with pytest.warns( + UserWarning, match="Requested adjoint differentiation to be computed with finite shots." + ): + self.calculate_jacobian(lightning_sv(num_wires=1), tape) + + @staticmethod + def calculate_jacobian(statevector, tape): + statevector = statevector.get_final_state(tape) + + return LightningAdjointJacobian(statevector).calculate_jacobian(tape) + + def test_empty_measurements(self, lightning_sv): + """Tests if an empty array is returned when the measurements of the tape is empty.""" + + with qml.tape.QuantumTape() as tape: + qml.RX(0.4, wires=[0]) + + jac = self.calculate_jacobian(lightning_sv(num_wires=3), tape) + assert len(jac) == 0 + + def test_unsupported_op(self, lightning_sv): + """Test if a QuantumFunctionError is raised for an unsupported operation, i.e., + multi-parameter operations that are not qml.Rot""" + + with qml.tape.QuantumTape() as tape: + qml.CRot(0.1, 0.2, 0.3, wires=[0, 1]) + qml.expval(qml.PauliZ(0)) + + with pytest.raises( + qml.QuantumFunctionError, match="The CRot operation is not supported using the" + ): + self.calculate_jacobian(lightning_sv(num_wires=3), tape) + + @pytest.mark.parametrize( + "op", [qml.Projector([0, 1], wires=[0, 1]), qml.Projector([0], wires=[0]) @ qml.PauliZ(0)] + ) + def test_proj_unsupported(self, op, lightning_sv): + """Test if a QuantumFunctionError is raised for a Projector observable""" + with qml.tape.QuantumTape() as tape: + qml.CRX(0.1, wires=[0, 1]) + qml.expval(op) + + with pytest.raises( + qml.QuantumFunctionError, match="differentiation method does not support the Projector" + ): + self.calculate_jacobian(lightning_sv(num_wires=3), tape) + + @pytest.mark.parametrize("n_qubits", [1, 2, 3, 4]) + @pytest.mark.parametrize("par", [-np.pi / 7, np.pi / 5, 2 * np.pi / 3]) + def test_phaseshift_gradient(self, n_qubits, par, tol, lightning_sv): + """Test that the gradient of the phaseshift gate matches the exact analytic formula.""" + par = np.array(par) + # dev = qml.device("lightning.qubit", wires=n_qubits) + init_state = np.zeros(2**n_qubits) + init_state[-2::] = np.array([1.0 / np.sqrt(2), 1.0 / np.sqrt(2)], requires_grad=False) + + with qml.tape.QuantumTape() as tape: + qml.StatePrep(init_state, wires=range(n_qubits)) + qml.ctrl(qml.PhaseShift(par, wires=n_qubits - 1), range(0, n_qubits - 1)) + qml.expval(qml.PauliY(n_qubits - 1)) + + tape.trainable_params = {1} + + expected = np.cos(par) + result = self.calculate_jacobian(lightning_sv(num_wires=n_qubits), tape) + + assert np.allclose(expected, result, atol=tol, rtol=0) + + @pytest.mark.parametrize("par", [1, -2, 1.623, -0.051, 0]) # integers, floats, zero + def test_ry_gradient(self, par, tol, lightning_sv): + """Test that the gradient of the RY gate matches the exact analytic formula.""" + with qml.tape.QuantumTape() as tape: + qml.RY(par, wires=[0]) + qml.expval(qml.PauliX(0)) + + tape.trainable_params = {0} + + expected = np.cos(par) + result = self.calculate_jacobian(lightning_sv(num_wires=3), tape) + + assert np.allclose(expected, result, atol=tol, rtol=0) + + def test_rx_gradient(self, tol, lightning_sv): + """Test that the gradient of the RX gate matches the known formula.""" + par = 0.7418 + + with qml.tape.QuantumTape() as tape: + qml.RX(par, wires=0) + qml.expval(qml.PauliZ(0)) + + expected = -np.sin(par) + result = self.calculate_jacobian(lightning_sv(num_wires=3), tape) + + assert np.allclose(expected, result, atol=tol, rtol=0) + + def test_multiple_rx_gradient_pauliz(self, tol, lightning_sv): + """Tests that the gradient of multiple RX gates in a circuit yields the correct result.""" + params = np.array([np.pi, np.pi / 2, np.pi / 3]) + + with qml.tape.QuantumTape() as tape: + qml.RX(params[0], wires=0) + qml.RX(params[1], wires=1) + qml.RX(params[2], wires=2) + + for idx in range(3): + qml.expval(qml.PauliZ(idx)) + + expected = -np.diag(np.sin(params)) + result = self.calculate_jacobian(lightning_sv(num_wires=3), tape) + + assert np.allclose(expected, result, atol=tol, rtol=0) + + def test_multiple_rx_gradient_hermitian(self, tol, lightning_sv): + """Tests that the gradient of multiple RX gates in a circuit yields the correct result + with Hermitian observable + """ + params = np.array([np.pi, np.pi / 2, np.pi / 3]) + + with qml.tape.QuantumTape() as tape: + qml.RX(params[0], wires=0) + qml.RX(params[1], wires=1) + qml.RX(params[2], wires=2) + + for idx in range(3): + qml.expval(qml.Hermitian([[1, 0], [0, -1]], wires=[idx])) + + tape.trainable_params = {0, 1, 2} + + expected = -np.diag(np.sin(params)) + result = self.calculate_jacobian(lightning_sv(num_wires=3), tape) + + assert np.allclose(expected, result, atol=tol, rtol=0) + + def test_multiple_rx_gradient_expval_hermitian(self, tol, lightning_sv): + """Tests that the gradient of multiple RX gates in a circuit yields the correct result + with Hermitian observable + """ + params = np.array([np.pi / 3, np.pi / 4, np.pi / 5]) + + with qml.tape.QuantumTape() as tape: + qml.RX(params[0], wires=0) + qml.RX(params[1], wires=1) + qml.RX(params[2], wires=2) + + qml.expval( + qml.Hermitian( + [[1, 0, 0, 0], [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, 1]], wires=[0, 2] + ) + ) + + tape.trainable_params = {0, 1, 2} + + expected = np.array( + [-np.sin(params[0]) * np.cos(params[2]), 0, -np.cos(params[0]) * np.sin(params[2])] + ) + result = self.calculate_jacobian(lightning_sv(num_wires=3), tape) + + assert np.allclose(expected, result, atol=tol, rtol=0) + + def test_multiple_rx_gradient_expval_hamiltonian(self, tol, lightning_sv): + """Tests that the gradient of multiple RX gates in a circuit yields the correct result + with Hermitian observable + """ + params = np.array([np.pi / 3, np.pi / 4, np.pi / 5]) + + ham = qml.Hamiltonian( + [1.0, 0.3, 0.3, 0.4], + [ + qml.PauliX(0) @ qml.PauliX(1), + qml.PauliZ(0), + qml.PauliZ(1), + qml.Hermitian( + [[1, 0, 0, 0], [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, 1]], wires=[0, 2] + ), + ], + ) + + with qml.tape.QuantumTape() as tape: + qml.RX(params[0], wires=0) + qml.RX(params[1], wires=1) + qml.RX(params[2], wires=2) + + qml.expval(ham) + + tape.trainable_params = {0, 1, 2} + + expected = ( + 0.3 * np.array([-np.sin(params[0]), 0, 0]) + + 0.3 * np.array([0, -np.sin(params[1]), 0]) + + 0.4 + * np.array( + [-np.sin(params[0]) * np.cos(params[2]), 0, -np.cos(params[0]) * np.sin(params[2])] + ) + ) + result = self.calculate_jacobian(lightning_sv(num_wires=3), tape) + + assert np.allclose(expected, result, atol=tol, rtol=0) + + +def simple_circuit_ansatz(params, wires): + """Circuit ansatz containing a large circuit""" + return [ + qml.QubitStateVector(unitary_group.rvs(2**4, random_state=0)[0], wires=wires), + qml.RX(params[0], wires=wires[0]), + qml.RY(params[1], wires=wires[1]), + qml.RZ(params[2], wires=wires[3]), + qml.CRX(params[3], wires=[wires[3], wires[0]]), + qml.CRY(params[4], wires=[wires[2], wires[1]]), + qml.CRZ(params[5], wires=[wires[2], wires[1]]), + qml.PhaseShift(params[6], wires=wires[2]), + qml.MultiRZ(params[7], wires=[wires[0], wires[1]]), + qml.IsingXX(params[8], wires=[wires[1], wires[0]]), + qml.IsingXY(params[9], wires=[wires[3], wires[2]]), + qml.IsingYY(params[10], wires=[wires[3], wires[2]]), + qml.IsingZZ(params[11], wires=[wires[2], wires[1]]), + ] + + +# fmt: off +expected_jac_simple_circuit_ansatz = np.array([[ + -9.77334961e-03, -1.30657957e-01, -1.66427588e-17, + 1.66379059e-01, -3.56645181e-02, -1.95066583e-01, + -1.65685685e-16, 4.85722573e-17, -3.56748062e-01, + -1.73472348e-17, 2.94902991e-17, -2.44233119e-18], + [-5.55111512e-17, 2.22411150e-02, -3.47974591e-17, + 6.93889390e-18, 1.50214879e-01, -1.86416270e-01, + -1.29381272e-16, 0.00000000e+00, 8.63611865e-02, + -2.42861287e-17, 5.55111512e-17, 1.90053083e-17]]) +# fmt: on + + +def test_large_circuit(tol, lightning_sv): + """Test the adjoint Jacobian pipeline for a large circuit with multiple expectation values. + When batch_obs=True, expvals are generated in parallelized chunks. + Expected results were calculated with default.qubit""" + statevector = lightning_sv(num_wires=4) + + n_params = 12 + params = np.linspace(0, 10, n_params) + tape = QuantumScript( + simple_circuit_ansatz(params, wires=range(4)), [qml.expval(qml.PauliZ(i)) for i in range(2)] + ) + + tape.trainable_params = set(range(1, n_params + 1)) + + statevector = statevector.get_final_state(tape) + + jac = LightningAdjointJacobian(statevector).calculate_jacobian(tape) + batched_jac = LightningAdjointJacobian(statevector, batch_obs=True).calculate_jacobian(tape) + + assert np.allclose(expected_jac_simple_circuit_ansatz, jac, atol=tol) + assert np.allclose(expected_jac_simple_circuit_ansatz, batched_jac, atol=tol) + + +class TestVectorJacobianProduct: + """Tests for the `vjp` functionality""" + + @staticmethod + def calculate_jacobian(statevector, tape): + statevector = statevector.get_final_state(tape) + + return LightningAdjointJacobian(statevector).calculate_jacobian(tape) + + @staticmethod + def calculate_vjp(statevector, tape, vector): + statevector = statevector.get_final_state(tape) + + return LightningAdjointJacobian(statevector).calculate_vjp(tape, vector) + + def test_multiple_measurements(self, tol, lightning_sv): + """Tests provides correct answer when provided multiple measurements.""" + x, y, z = [0.5, 0.3, -0.7] + + with qml.tape.QuantumTape() as tape1: + qml.RX(0.4, wires=[0]) + qml.Rot(x, y, z, wires=[0]) + qml.RY(-0.2, wires=[0]) + qml.expval(qml.PauliX(0)) + qml.expval(qml.PauliY(1)) + qml.expval(qml.PauliZ(1)) + + dy = np.array([1.0, 2.0, 3.0]) + tape1.trainable_params = {1, 2, 3} + + with qml.tape.QuantumTape() as tape2: + ham = qml.Hamiltonian(dy, [qml.PauliX(0), qml.PauliY(1), qml.PauliY(1)]) + qml.RX(0.4, wires=[0]) + qml.Rot(x, y, z, wires=[0]) + qml.RY(-0.2, wires=[0]) + qml.expval(ham) + + tape2.trainable_params = {1, 2, 3} + + statevector = lightning_sv(num_wires=2) + result_vjp = self.calculate_vjp(statevector, tape1, dy) + + statevector.reset_state() + + result_jac = self.calculate_jacobian(statevector, tape2) + + assert np.allclose(result_vjp, result_jac, atol=tol, rtol=0) + + def test_wrong_dy_expval(self, lightning_sv): + """Tests raise an exception when dy is incorrect""" + statevector = lightning_sv(num_wires=2) + + x, y, z = [0.5, 0.3, -0.7] + + with qml.tape.QuantumTape() as tape1: + qml.RX(0.4, wires=[0]) + qml.Rot(x, y, z, wires=[0]) + qml.RY(-0.2, wires=[0]) + qml.expval(qml.PauliX(0)) + qml.expval(qml.PauliY(1)) + qml.expval(qml.PauliZ(1)) + + dy1 = np.array([1.0, 2.0]) + tape1.trainable_params = {1, 2, 3} + + with pytest.raises( + ValueError, match="Number of observables in the tape must be the same as" + ): + self.calculate_vjp(statevector, tape1, dy1) + + dy2 = np.array([1.0 + 3.0j, 0.3 + 2.0j, 0.5 + 0.1j]) + with pytest.raises( + ValueError, match="The vjp method only works with a real-valued grad_vec" + ): + self.calculate_vjp(statevector, tape1, dy2) + + def test_not_expval(self, lightning_sv): + """Test if a QuantumFunctionError is raised for a tape with measurements that are not + expectation values""" + statevector = lightning_sv(num_wires=2) + + with qml.tape.QuantumTape() as tape: + qml.RX(0.1, wires=0) + qml.var(qml.PauliZ(0)) + + dy = np.array([1.0]) + + with pytest.raises( + qml.QuantumFunctionError, match="Adjoint differentiation method does not" + ): + self.calculate_vjp(statevector, tape, dy) + + def test_finite_shots_warns(self, lightning_sv): + """Tests warning raised when finite shots specified""" + + statevector = lightning_sv(num_wires=2) + + with qml.tape.QuantumTape() as tape: + qml.expval(qml.PauliZ(0)) + + dy = np.array([1.0]) + + with pytest.warns( + UserWarning, + match="Requested adjoint differentiation to be computed with finite shots.", + ): + self.calculate_vjp(statevector, tape, dy) + + def test_unsupported_op(self, lightning_sv): + """Test if a QuantumFunctionError is raised for an unsupported operation, i.e., + multi-parameter operations that are not qml.Rot""" + + statevector = lightning_sv(num_wires=2) + + with qml.tape.QuantumTape() as tape: + qml.CRot(0.1, 0.2, 0.3, wires=[0, 1]) + qml.expval(qml.PauliZ(0)) + + dy = np.array([1.0]) + + with pytest.raises( + qml.QuantumFunctionError, + match="The CRot operation is not supported using the", + ): + self.calculate_vjp(statevector, tape, dy) + + def test_proj_unsupported(self, lightning_sv): + """Test if a QuantumFunctionError is raised for a Projector observable""" + + statevector = lightning_sv(num_wires=2) + + with qml.tape.QuantumTape() as tape: + qml.CRX(0.1, wires=[0, 1]) + qml.expval(qml.Projector([0, 1], wires=[0, 1])) + + dy = np.array([1.0]) + + with pytest.raises( + qml.QuantumFunctionError, + match="differentiation method does not support the Projector", + ): + self.calculate_vjp(statevector, tape, dy) + + statevector.reset_state() + + with qml.tape.QuantumTape() as tape: + qml.CRX(0.1, wires=[0, 1]) + qml.expval(qml.Projector([0], wires=[0]) @ qml.PauliZ(0)) + + with pytest.raises( + qml.QuantumFunctionError, + match="differentiation method does not support the Projector", + ): + self.calculate_vjp(statevector, tape, dy) + + def test_hermitian_expectation(self, tol, lightning_sv): + statevector = lightning_sv(num_wires=2) + + obs = np.array([[1, 0], [0, -1]], dtype=statevector.dtype, requires_grad=False) + dy = np.array([0.8]) + + for x in np.linspace(-2 * math.pi, 2 * math.pi, 7): + with qml.tape.QuantumTape() as tape: + qml.RY(x, wires=(0,)) + qml.expval(qml.Hermitian(obs, wires=(0,))) + tape.trainable_params = {0} + + statevector.reset_state() + vjp = self.calculate_vjp(statevector, tape, dy) + + assert np.allclose(vjp, -0.8 * np.sin(x), atol=tol) + + def test_hermitian_tensor_expectation(self, tol, lightning_sv): + statevector = lightning_sv(num_wires=2) + + obs = np.array([[1, 0], [0, -1]], dtype=statevector.dtype, requires_grad=False) + dy = np.array([0.8]) + + for x in np.linspace(-2 * math.pi, 2 * math.pi, 7): + with qml.tape.QuantumTape() as tape: + qml.RY(x, wires=(0,)) + qml.expval(qml.Hermitian(obs, wires=(0,)) @ qml.PauliZ(wires=1)) + tape.trainable_params = {0} + + statevector.reset_state() + vjp = self.calculate_vjp(statevector, tape, dy) + + assert np.allclose(vjp, -0.8 * np.sin(x), atol=tol) + + def test_state_measurement_not_supported(self, lightning_sv): + """Tests raise an exception when dy is incorrect""" + statevector = lightning_sv(num_wires=2) + + x, y, z = [0.5, 0.3, -0.7] + + with qml.tape.QuantumTape() as tape: + qml.RX(0.4, wires=[0]) + qml.Rot(x, y, z, wires=[0]) + qml.RY(-0.2, wires=[0]) + qml.state() + + tape.trainable_params = {1, 2, 3} + + dy = np.ones(3, dtype=statevector.dtype) + + with pytest.raises( + qml.QuantumFunctionError, + match="Adjoint differentiation does not support State measurements.", + ): + self.calculate_vjp(statevector, tape, dy) + + def test_no_trainable_parameters(self, lightning_sv): + """A tape with no trainable parameters will simply return None""" + statevector = lightning_sv(num_wires=2) + x = 0.4 + + with qml.tape.QuantumTape() as tape: + qml.RX(x, wires=0) + qml.CNOT(wires=[0, 1]) + qml.expval(qml.PauliZ(0)) + + tape.trainable_params = {} + dy = np.array([1.0]) + + vjp = self.calculate_vjp(statevector, tape, dy) + + assert len(vjp) == 0 + + def test_zero_dy(self, lightning_sv): + """A zero dy vector will return no tapes and a zero matrix""" + statevector = lightning_sv(num_wires=2) + x = 0.4 + y = 0.6 + + with qml.tape.QuantumTape() as tape: + qml.RX(x, wires=0) + qml.RX(y, wires=0) + qml.CNOT(wires=[0, 1]) + qml.expval(qml.PauliZ(0)) + + tape.trainable_params = {0, 1} + dy = np.array([0.0]) + + vjp = self.calculate_vjp(statevector, tape, dy) + + assert np.all(vjp == np.zeros([len(tape.trainable_params)])) + + def test_single_expectation_value(self, tol, lightning_sv): + """Tests correct output shape and evaluation for a tape + with a single expval output""" + statevector = lightning_sv(num_wires=2) + x = 0.543 + y = -0.654 + + with qml.tape.QuantumTape() as tape: + qml.RX(x, wires=[0]) + qml.RY(y, wires=[1]) + qml.CNOT(wires=[0, 1]) + qml.expval(qml.PauliZ(0) @ qml.PauliX(1)) + + tape.trainable_params = {0, 1} + dy = np.array([1.0]) + + vjp = self.calculate_vjp(statevector, tape, dy) + + expected = np.array([-np.sin(y) * np.sin(x), np.cos(y) * np.cos(x)]) + assert np.allclose(vjp, expected, atol=tol, rtol=0) + + def test_multiple_expectation_values(self, tol, lightning_sv): + """Tests correct output shape and evaluation for a tape + with multiple expval outputs""" + statevector = lightning_sv(num_wires=2) + x = 0.543 + y = -0.654 + + with qml.tape.QuantumTape() as tape: + qml.RX(x, wires=[0]) + qml.RY(y, wires=[1]) + qml.CNOT(wires=[0, 1]) + qml.expval(qml.PauliZ(0)) + qml.expval(qml.PauliX(1)) + + tape.trainable_params = {0, 1} + dy = np.array([1.0, 2.0]) + + vjp = self.calculate_vjp(statevector, tape, dy) + + expected = np.array([-np.sin(x), 2 * np.cos(y)]) + assert np.allclose(vjp, expected, atol=tol, rtol=0) + + def test_prob_expectation_values(self, lightning_sv): + """Tests correct output shape and evaluation for a tape + with prob and expval outputs""" + statevector = lightning_sv(num_wires=2) + + x = 0.543 + y = -0.654 + + with qml.tape.QuantumTape() as tape: + qml.RX(x, wires=[0]) + qml.RY(y, wires=[1]) + qml.CNOT(wires=[0, 1]) + qml.expval(qml.PauliZ(0)) + qml.probs(wires=[0, 1]) + + tape.trainable_params = {0, 1} + dy = np.array([1.0, 2.0, 3.0, 4.0, 5.0]) + + with pytest.raises( + qml.QuantumFunctionError, + match="Adjoint differentiation method does not support", + ): + self.calculate_vjp(statevector, tape, dy) From 724532bef6ab68ccdd492897c929785367c8ecfc Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Thu, 7 Mar 2024 10:32:10 -0500 Subject: [PATCH 145/428] format --- .../lightning_kokkos/lightning_kokkos.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pennylane_lightning/lightning_kokkos/lightning_kokkos.py b/pennylane_lightning/lightning_kokkos/lightning_kokkos.py index 9953e2651c..4878431445 100644 --- a/pennylane_lightning/lightning_kokkos/lightning_kokkos.py +++ b/pennylane_lightning/lightning_kokkos/lightning_kokkos.py @@ -64,6 +64,13 @@ from pennylane.ops.op_math import Adjoint from pennylane.wires import Wires + # pylint: disable=import-error, no-name-in-module, ungrouped-imports + from pennylane_lightning.core._serialize import ( + QuantumScriptSerializer, + global_phase_diagonal, + ) + from pennylane_lightning.core._version import __version__ + # pylint: disable=import-error, no-name-in-module, ungrouped-imports from pennylane_lightning.lightning_kokkos_ops.algorithms import ( AdjointJacobianC64, @@ -72,13 +79,6 @@ create_ops_listC128, ) - # pylint: disable=import-error, no-name-in-module, ungrouped-imports - from pennylane_lightning.core._serialize import ( - QuantumScriptSerializer, - global_phase_diagonal, - ) - from pennylane_lightning.core._version import __version__ - def _kokkos_dtype(dtype): if dtype not in [np.complex128, np.complex64]: # pragma: no cover raise ValueError(f"Data type is not supported for state-vector computation: {dtype}") From e938f975dfdaf02cd90ff721dfe028a55c78d20c Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Thu, 7 Mar 2024 10:32:59 -0500 Subject: [PATCH 146/428] add changelog for PR #613 --- .github/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index 66cb3090c6..858e9366da 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -2,6 +2,9 @@ ### New features since last release +* Add two new python classes (LightningStateVector and LightningMeasurements)to support the simulate logic for `lightning.qubit2`. + [(#613)](https://github.com/PennyLaneAI/pennylane-lightning/pull/613) + * Add analytic-mode `qml.probs` and `qml.var` support in `lightning.qubit2`. [(#627)](https://github.com/PennyLaneAI/pennylane-lightning/pull/627) From a95b5b1daba919149dadbbcfb88b4da3b914bc85 Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Thu, 7 Mar 2024 10:36:44 -0500 Subject: [PATCH 147/428] [skip ci] Added skeleton file for LQ2 unit tests --- .../lightning_qubit2/test_lightning_qubit2.py | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 tests/lightning_qubit2/test_lightning_qubit2.py diff --git a/tests/lightning_qubit2/test_lightning_qubit2.py b/tests/lightning_qubit2/test_lightning_qubit2.py new file mode 100644 index 0000000000..4b040ce2e3 --- /dev/null +++ b/tests/lightning_qubit2/test_lightning_qubit2.py @@ -0,0 +1,71 @@ +# Copyright 2018-2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This module contains unit tests for the LightningQubit2 class +""" + +import pytest + +import numpy as np +import pennylane as qml +from pennylane_lightning.lightning_qubit import LightningQubit, LightningQubit2 +from pennylane_lightning.lightning_qubit.lightning_qubit2 import ( + simulate, + jacobian, + simulate_and_jacobian, +) +from pennylane.devices import DefaultQubit + +from conftest import LightningDevice # tested device + +if LightningDevice != LightningQubit: + pytest.skip("Exclusive tests for lightning.qubit. Skipping.", allow_module_level=True) + +if not LightningQubit2._CPP_BINARY_AVAILABLE: + pytest.skip("No binary module found. Skipping.", allow_module_level=True) + +THETA = np.linspace(0.11, 1, 3) +PHI = np.linspace(0.32, 1, 3) +VARPHI = np.linspace(0.02, 1, 3) + + +class TestHelpers: + """Unit tests for the simulate function""" + + # Test simulate + # Test jacobian + xfail tests + # Test simulate_and_jacobian + xfail tests + # Test stopping_condition + # Test accepted_observables + + +class TestInitialization: + """Unit tests for LightningQubit2 initialization""" + + # Test __init__ errors: invalid num_burnin, kernel name + + +class TestExecution: + """Unit tests for executing quantum tapes on LightningQubit2""" + + # Test preprocess + # Test execute + + +class TestDerivatives: + """Unit tests for calculating derivatives with LightningQubit2""" + + # Test supports derivative + xfail tests + # Test compute_derivatives + xfail tests + # Test execute_and_compute_derivatives + xfail tests From a1d5f14976f1114567658fcd66b9a44398d30f53 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Thu, 7 Mar 2024 10:36:52 -0500 Subject: [PATCH 148/428] update changelog --- .github/CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index 858e9366da..60c1fa173d 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -2,12 +2,15 @@ ### New features since last release -* Add two new python classes (LightningStateVector and LightningMeasurements)to support the simulate logic for `lightning.qubit2`. +* Add two new python classes (LightningStateVector and LightningMeasurements)to support `lightning.qubit2`. [(#613)](https://github.com/PennyLaneAI/pennylane-lightning/pull/613) * Add analytic-mode `qml.probs` and `qml.var` support in `lightning.qubit2`. [(#627)](https://github.com/PennyLaneAI/pennylane-lightning/pull/627) +* Add LightningAdjointJacobian to support `lightning.qubit2`. + [(#631)](https://github.com/PennyLaneAI/pennylane-lightning/pull/631) + ### Breaking changes ### Improvements From 424c8e99530b31416be1b9e19f1c1a6e2228479b Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Thu, 7 Mar 2024 10:40:43 -0500 Subject: [PATCH 149/428] update adjoint Jacobian --- pennylane_lightning/lightning_qubit/_adjoint_jacobian.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/_adjoint_jacobian.py b/pennylane_lightning/lightning_qubit/_adjoint_jacobian.py index bb220003c0..54a2953e46 100644 --- a/pennylane_lightning/lightning_qubit/_adjoint_jacobian.py +++ b/pennylane_lightning/lightning_qubit/_adjoint_jacobian.py @@ -136,7 +136,7 @@ def _check_adjdiff_supported_operations(operations): def _process_jacobian_tape( self, tape: QuantumTape, use_mpi: bool = False, split_obs: bool = False ): - use_csingle = True if self._dtype == np.complex64 else False + use_csingle = (self._dtype == np.complex64) obs_serialized, obs_idx_offsets = QuantumScriptSerializer( self._qubit_state.device_name, use_csingle, use_mpi, split_obs @@ -263,7 +263,7 @@ def calculate_jacobian(self, tape: QuantumTape): return self._adjoint_jacobian_processing(jac_r) # pylint: disable=line-too-long, inconsistent-return-statements - def calculate_vjp(self, tape, grad_vec): + def calculate_vjp(self, tape: QuantumTape, grad_vec): """Generate the processing function required to compute the vector-Jacobian products of a tape. @@ -329,7 +329,7 @@ def processing_fn_expval(tape): return np.array([], dtype=self.qubit_state.dtype) new_tape = tape.copy() - new_tape._measurements = [qml.expval(ham)] + new_tape.measurements = [qml.expval(ham)] return self.calculate_jacobian(new_tape) From c91ff39f14b3a0a276ae6f44465c08f4caf682b9 Mon Sep 17 00:00:00 2001 From: Dev version update bot Date: Thu, 7 Mar 2024 15:41:16 +0000 Subject: [PATCH 150/428] Auto update version --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index b8174c108a..25f4c22d24 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.36.0-dev3" +__version__ = "0.36.0-dev4" From b005b9b723e09dfde672da085f9a9c3c5a5cad1c Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Thu, 7 Mar 2024 10:42:01 -0500 Subject: [PATCH 151/428] codefactor --- pennylane_lightning/lightning_qubit/_adjoint_jacobian.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/lightning_qubit/_adjoint_jacobian.py b/pennylane_lightning/lightning_qubit/_adjoint_jacobian.py index 54a2953e46..98687b5759 100644 --- a/pennylane_lightning/lightning_qubit/_adjoint_jacobian.py +++ b/pennylane_lightning/lightning_qubit/_adjoint_jacobian.py @@ -136,7 +136,7 @@ def _check_adjdiff_supported_operations(operations): def _process_jacobian_tape( self, tape: QuantumTape, use_mpi: bool = False, split_obs: bool = False ): - use_csingle = (self._dtype == np.complex64) + use_csingle = self._dtype == np.complex64 obs_serialized, obs_idx_offsets = QuantumScriptSerializer( self._qubit_state.device_name, use_csingle, use_mpi, split_obs From 1d6afc22c8a64545fa6564cf4aa7723ab48cb91a Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Thu, 7 Mar 2024 16:02:33 +0000 Subject: [PATCH 152/428] Add shots tests and fix bugs in LQ, LQ2. --- .github/CHANGELOG.md | 9 +++ .../lightning_qubit/lightning_qubit.py | 35 +++++--- .../lightning_qubit/lightning_qubit2.py | 3 +- tests/conftest.py | 81 +++++++++++++++++++ tests/test_measurements.py | 39 ++++++++- 5 files changed, 153 insertions(+), 14 deletions(-) diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index a807815c9b..d2de3ab8c3 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -2,6 +2,12 @@ ### New features since last release +* Add finite shots support in `lightning.qubit2`. + [(#630)](https://github.com/PennyLaneAI/pennylane-lightning/pull/630) + +* Add analytic-mode `qml.probs` and `qml.var` support in `lightning.qubit2`. + [(#627)](https://github.com/PennyLaneAI/pennylane-lightning/pull/627) + ### Breaking changes ### Improvements @@ -13,6 +19,9 @@ ### Bug fixes +* Fix finite shots support in `lightning.qubit`. + [(#630)](https://github.com/PennyLaneAI/pennylane-lightning/pull/630) + ### Contributors This release contains contributions from (in alphabetical order): diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit.py b/pennylane_lightning/lightning_qubit/lightning_qubit.py index f220653311..79c1e319f8 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit.py @@ -18,6 +18,7 @@ """ from pathlib import Path +from typing import Sequence from warnings import warn import numpy as np @@ -247,7 +248,8 @@ def __init__( # pylint: disable=too-many-arguments f"The {kernel_name} is not supported and currently " "only 'Local' and 'NonZeroRandom' kernels are supported." ) - if num_burnin >= shots: + shots = shots if isinstance(shots, Sequence) else [shots] + if any(num_burnin >= s for s in shots): raise ValueError("Shots should be greater than num_burnin.") self._kernel_name = kernel_name self._num_burnin = num_burnin @@ -494,10 +496,13 @@ def expval(self, observable, shot_range=None, bin_size=None): if observable.name in [ "Projector", ]: - if self.shots is None: - qs = qml.tape.QuantumScript([], [qml.expval(observable)]) - self.apply(self._get_diagonalizing_gates(qs)) - return super().expval(observable, shot_range=shot_range, bin_size=bin_size) + diagonalizing_gates = observable.diagonalizing_gates() + if self.shots is None and diagonalizing_gates: + self.apply_lightning(diagonalizing_gates) + results = super().expval(observable, shot_range=shot_range, bin_size=bin_size) + if diagonalizing_gates: + self.apply_lightning([qml.adjoint(g, lazy=False) for g in diagonalizing_gates]) + return results if self.shots is not None: # estimate the expectation value @@ -550,10 +555,13 @@ def var(self, observable, shot_range=None, bin_size=None): if observable.name in [ "Projector", ]: - if self.shots is None: - qs = qml.tape.QuantumScript([], [qml.var(observable)]) - self.apply(self._get_diagonalizing_gates(qs)) - return super().var(observable, shot_range=shot_range, bin_size=bin_size) + diagonalizing_gates = observable.diagonalizing_gates() + if self.shots is None and diagonalizing_gates: + self.apply_lightning(diagonalizing_gates) + results = super().var(observable, shot_range=shot_range, bin_size=bin_size) + if diagonalizing_gates: + self.apply_lightning([qml.adjoint(g, lazy=False) for g in diagonalizing_gates]) + return results if self.shots is not None: # estimate the var @@ -629,9 +637,12 @@ def probability_lightning(self, wires): # pylint: disable=attribute-defined-outside-init def sample(self, observable, shot_range=None, bin_size=None, counts=False): """Return samples of an observable.""" - if not isinstance(observable, qml.PauliZ): - self.apply_lightning(observable.diagonalizing_gates()) - self._samples = self.generate_samples() + diagonalizing_gates = observable.diagonalizing_gates() + if diagonalizing_gates: + self.apply_lightning(diagonalizing_gates) + self._samples = self.generate_samples() + if diagonalizing_gates: + self.apply_lightning([qml.adjoint(g, lazy=False) for g in diagonalizing_gates]) return super().sample( observable, shot_range=shot_range, bin_size=bin_size, counts=counts ) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index b80c64ff74..1c41df0b82 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -246,7 +246,8 @@ def __init__( # pylint: disable=too-many-arguments f"The {kernel_name} is not supported and currently " "only 'Local' and 'NonZeroRandom' kernels are supported." ) - if num_burnin >= shots: + shots = shots if isinstance(shots, Sequence) else [shots] + if any(num_burnin >= s for s in shots): raise ValueError("Shots should be greater than num_burnin.") self._kernel_name = kernel_name self._num_burnin = num_burnin diff --git a/tests/conftest.py b/tests/conftest.py index d3cef75d53..918a0ebc30 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -15,6 +15,8 @@ Pytest configuration file for PennyLane-Lightning test suite. """ import os +from functools import reduce +from typing import Sequence import numpy as np import pennylane as qml @@ -153,3 +155,82 @@ def _device(wires, shots=None): return qml.device(device_name, wires=wires, shots=shots, c_dtype=request.param) return _device + + +def validate_counts(shots, results1, results2): + """Compares two counts. + + If the results are ``Sequence``s, loop over entries. + + Fails if a key of ``results1`` is not found in ``results2``. + Passes if counts are too low, chosen as ``100``. + Otherwise, fails if counts differ by more than ``20`` plus 20 percent. + """ + if isinstance(results1, Sequence): + assert isinstance(results2, Sequence) + assert len(results1) == len(results2) + for r1, r2 in zip(results1, results2): + validate_counts(shots, r1, r2) + return + for key1, val1 in results1.items(): + val2 = results2[key1] + if abs(val1 + val2) > 100: + assert np.allclose(val1, val2, rtol=20, atol=0.2) + + +def validate_samples(shots, results1, results2): + """Compares two samples. + + If the results are ``Sequence``s, loop over entries. + + Fails if the results do not have the same shape, within ``20`` entries plus 20 percent. + This is to handle cases when post-selection yields variable shapes. + Otherwise, fails if the sums of samples differ by more than ``20`` plus 20 percent. + """ + if isinstance(shots, Sequence): + assert isinstance(results1, Sequence) + assert isinstance(results2, Sequence) + assert len(results1) == len(results2) + for s, r1, r2 in zip(shots, results1, results2): + validate_samples(s, r1, r2) + else: + sh1, sh2 = results1.shape[0], results2.shape[0] + assert np.allclose(sh1, sh2, rtol=20, atol=0.2) + assert results1.ndim == results2.ndim + if results2.ndim > 1: + assert results1.shape[1] == results2.shape[1] + np.allclose(np.sum(results1), np.sum(results2), rtol=20, atol=0.2) + + +def validate_expval(shots, results1, results2): + """Compares two expval, probs or var. + + If the results are ``Sequence``s, validate the average of items. + + If ``shots is None``, validate using ``np.allclose``'s default parameters. + Otherwise, fails if the results do not match within ``0.01`` plus 20 percent. + """ + if isinstance(results1, Sequence): + assert isinstance(results2, Sequence) + assert len(results1) == len(results2) + results1 = reduce(lambda x, y: x + y, results1) / len(results1) + results2 = reduce(lambda x, y: x + y, results2) / len(results2) + validate_expval(shots, results1, results2) + return + if shots is None: + assert np.allclose(results1, results2) + return + assert np.allclose(results1, results2, atol=0.01, rtol=0.2) + + +def validate_measurements(func, shots, results1, results2): + """Calls the correct validation function based on measurement type.""" + if func is qml.counts: + validate_counts(shots, results1, results2) + return + + if func is qml.sample: + validate_samples(shots, results1, results2) + return + + validate_expval(shots, results1, results2) diff --git a/tests/test_measurements.py b/tests/test_measurements.py index 7b7b36525e..3cf25b300e 100644 --- a/tests/test_measurements.py +++ b/tests/test_measurements.py @@ -15,12 +15,14 @@ Unit tests for Measurements in Lightning devices. """ import math +from typing import Sequence import numpy as np import pennylane as qml import pytest from conftest import LightningDevice as ld -from conftest import device_name, lightning_ops +from conftest import device_name, lightning_ops, validate_measurements +from flaky import flaky from pennylane.measurements import Expectation, Variance if not ld._CPP_BINARY_AVAILABLE: @@ -684,3 +686,38 @@ def circuit2(): return [qml.var(qml.PauliZ(wires=w)) for w in wires2] assert np.allclose(circuit1(), circuit2(), atol=tol) + + +@flaky(max_runs=5) +@pytest.mark.parametrize("shots", [10000, [10000, 11111]]) +@pytest.mark.parametrize("measure_f", [qml.counts, qml.expval, qml.probs, qml.sample, qml.var]) +@pytest.mark.parametrize( + "obs", [[0], [0, 1], qml.PauliZ(0), qml.PauliY(1), qml.PauliZ(0) @ qml.PauliY(1)] +) +@pytest.mark.parametrize("mcmc", [False, True]) +@pytest.mark.parametrize("kernel_name", ["Local", "NonZeroRandom"]) +def test_shots_single_measure_obs(shots, measure_f, obs, mcmc, kernel_name): + """Tests that Lightning handles shots in a circuit where a single measurement of a common observable is performed at the end.""" + n_qubits = 2 + + if measure_f in (qml.expval, qml.var) and isinstance(obs, Sequence): + pytest.skip("qml.expval, qml.var do not take wire arguments.") + + dev = qml.device(device_name, wires=n_qubits, shots=shots, mcmc=mcmc, kernel_name=kernel_name) + dq = qml.device("default.qubit", wires=n_qubits, shots=shots) + params = [np.pi / 4, -np.pi / 4] + + def func(x, y): + for i in range(n_qubits): + qml.Hadamard(i) + qml.RX(x, 0) + qml.RX(y, 1) + return measure_f(wires=obs) if isinstance(obs, Sequence) else measure_f(op=obs) + + func1 = qml.QNode(func, dev) + results1 = func1(*params) + + func2 = qml.QNode(func, dq) + results2 = func2(*params) + + validate_measurements(measure_f, shots, results1, results2) From 7dd8b841df44198280b4ec27f7b9c34a4e2a2b42 Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Thu, 7 Mar 2024 11:44:49 -0500 Subject: [PATCH 153/428] Lightning qubit2 upgrade api (#628) * update * adding tests from add-simulate branch * merge conflicts * create state vector on initialization * remove import of modifier from lightning * Update pennylane_lightning/lightning_qubit/lightning_qubit2.py * minor test updates * register with setup.py, state vector fixes * add LightningQubit2 to init and format * add cpp binary available variable * Auto update version * reduce dependency on DefaultQubit for tests * update LightningQubit2 * Introduce _new_API and fix/skip few tests. * Fix few more tests. * Skip shots, adjoint, vjp with new API. * Fix no-bin interface. * Remove duplicate class data. * Include LQ2 in linux ests. * --cov-append --------- Co-authored-by: albi3ro Co-authored-by: AmintorDusko Co-authored-by: Dev version update bot --- .github/workflows/tests_linux.yml | 1 + pennylane_lightning/core/_serialize.py | 2 +- pennylane_lightning/core/lightning_base.py | 6 ++- .../lightning_gpu/lightning_gpu.py | 2 +- .../lightning_qubit/lightning_qubit2.py | 48 +++++++++++++------ pennylane_lightning/lightning_qubit2 | 1 + setup.py | 1 + tests/conftest.py | 7 ++- .../test_measurements_class.py | 40 ++++++++++++++-- tests/lightning_qubit2/test_expval_2.py | 7 ++- tests/test_adjoint_jacobian.py | 3 ++ tests/test_apply.py | 47 ++++++++++++++---- tests/test_decomposition.py | 1 + tests/test_device.py | 2 +- tests/test_expval.py | 12 ++++- tests/test_gates.py | 9 ++-- tests/test_measurements.py | 17 +++++-- tests/test_var.py | 4 +- tests/test_vjp.py | 3 ++ 19 files changed, 165 insertions(+), 48 deletions(-) create mode 120000 pennylane_lightning/lightning_qubit2 diff --git a/.github/workflows/tests_linux.yml b/.github/workflows/tests_linux.yml index 91bd44bb85..b334c0acce 100644 --- a/.github/workflows/tests_linux.yml +++ b/.github/workflows/tests_linux.yml @@ -185,6 +185,7 @@ jobs: cd main/ DEVICENAME=`echo ${{ matrix.pl_backend }} | sed "s/_/./g"` PL_DEVICE=${DEVICENAME} python -m pytest tests/ $COVERAGE_FLAGS + PL_DEVICE=${DEVICENAME}2 python -m pytest tests/ $COVERAGE_FLAGS --cov-append pl-device-test --device ${DEVICENAME} --skip-ops --shots=20000 $COVERAGE_FLAGS --cov-append pl-device-test --device ${DEVICENAME} --shots=None --skip-ops $COVERAGE_FLAGS --cov-append mv .coverage .coverage-${{ github.job }}-${{ matrix.pl_backend }} diff --git a/pennylane_lightning/core/_serialize.py b/pennylane_lightning/core/_serialize.py index 2c85333b05..2eb0078aa5 100644 --- a/pennylane_lightning/core/_serialize.py +++ b/pennylane_lightning/core/_serialize.py @@ -60,7 +60,7 @@ def __init__( self.use_csingle = use_csingle self.device_name = device_name self.split_obs = split_obs - if device_name == "lightning.qubit": + if device_name in ("lightning.qubit", "lightning.qubit2"): try: import pennylane_lightning.lightning_qubit_ops as lightning_ops except ImportError as exception: diff --git a/pennylane_lightning/core/lightning_base.py b/pennylane_lightning/core/lightning_base.py index 36fa4c504f..88f2d78030 100644 --- a/pennylane_lightning/core/lightning_base.py +++ b/pennylane_lightning/core/lightning_base.py @@ -60,6 +60,7 @@ class LightningBase(QubitDevice): author = "Xanadu Inc." short_name = "lightning.base" _CPP_BINARY_AVAILABLE = True + _new_API = False def __init__( self, @@ -76,7 +77,7 @@ def __init__( r_dtype = np.float64 self.use_csingle = False else: - raise TypeError(f"Unsupported complex Type: {c_dtype}") + raise TypeError(f"Unsupported complex type: {c_dtype}") super().__init__(wires, shots=shots, r_dtype=r_dtype, c_dtype=c_dtype) self._batch_obs = batch_obs @@ -396,6 +397,7 @@ class LightningBaseFallBack(DefaultQubitLegacy): # pragma: no cover version = __version__ author = "Xanadu Inc." _CPP_BINARY_AVAILABLE = False + _new_API = False def __init__(self, wires, *, c_dtype=np.complex128, **kwargs): if c_dtype is np.complex64: @@ -403,7 +405,7 @@ def __init__(self, wires, *, c_dtype=np.complex128, **kwargs): elif c_dtype is np.complex128: r_dtype = np.float64 else: - raise TypeError(f"Unsupported complex Type: {c_dtype}") + raise TypeError(f"Unsupported complex type: {c_dtype}") super().__init__(wires, r_dtype=r_dtype, c_dtype=c_dtype, **kwargs) @property diff --git a/pennylane_lightning/lightning_gpu/lightning_gpu.py b/pennylane_lightning/lightning_gpu/lightning_gpu.py index be36ff548d..1a5a45798b 100644 --- a/pennylane_lightning/lightning_gpu/lightning_gpu.py +++ b/pennylane_lightning/lightning_gpu/lightning_gpu.py @@ -261,7 +261,7 @@ def __init__( elif c_dtype is np.complex128: self.use_csingle = False else: - raise TypeError(f"Unsupported complex Type: {c_dtype}") + raise TypeError(f"Unsupported complex type: {c_dtype}") super().__init__(wires, shots=shots, c_dtype=c_dtype) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index c2b3028640..10684e595a 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -13,32 +13,33 @@ # limitations under the License. """ This module contains the LightningQubit2 class that inherits from the new device interface. + """ -from typing import Optional, Union, Sequence, Callable from dataclasses import replace -import numpy as np +from pathlib import Path +from typing import Callable, Optional, Sequence, Union +import numpy as np import pennylane as qml -from pennylane.devices import Device, ExecutionConfig, DefaultExecutionConfig -from pennylane.devices.modifiers import single_tape_support, simulator_tracking +from pennylane.devices import DefaultExecutionConfig, Device, ExecutionConfig +from pennylane.devices.modifiers import simulator_tracking, single_tape_support from pennylane.devices.preprocess import ( decompose, + no_sampling, validate_device_wires, - decompose, validate_measurements, validate_observables, - no_sampling, ) -from pennylane.tape import QuantumTape, QuantumScript +from pennylane.tape import QuantumScript, QuantumTape from pennylane.transforms.core import TransformProgram from pennylane.typing import Result, ResultBatch -from ._state_vector import LightningStateVector from ._measurements import LightningMeasurements +from ._state_vector import LightningStateVector try: # pylint: disable=import-error, unused-import - import pennylane_lightning.lightning_qubit_ops + from pennylane_lightning.lightning_qubit_ops import backend_info LQ_CPP_BINARY_AVAILABLE = True except ImportError: @@ -52,12 +53,16 @@ def simulate(circuit: QuantumScript, state: LightningStateVector) -> Result: """Simulate a single quantum script. + Args: circuit (QuantumTape): The single circuit to simulate state (LightningStateVector): handle to Lightning state vector + Returns: tuple(TensorLike): The results of the simulation + Note that this function can return measurements for non-commuting observables simultaneously. + """ state.reset_state() final_state = state.get_final_state(circuit) @@ -153,6 +158,8 @@ def simulate_and_jacobian(circuit: QuantumTape): "QFT", "ECR", "BlockEncode", + "GlobalPhase", + "C(GlobalPhase)", } ) """The set of supported operations.""" @@ -194,7 +201,13 @@ class LightningQubit2(Device): _device_options = ("rng", "c_dtype", "batch_obs", "mcmc", "kernel_name", "num_burnin") + _new_API = True _CPP_BINARY_AVAILABLE = LQ_CPP_BINARY_AVAILABLE + short_name = "lightning.qubit2" + operations = _operations + observables = _observables + _backend_info = backend_info if LQ_CPP_BINARY_AVAILABLE else None + config = Path(__file__).parent / "lightning_qubit.toml" def __init__( # pylint: disable=too-many-arguments self, @@ -247,14 +260,19 @@ def c_dtype(self): return self._c_dtype @property - def operations(self) -> frozenset[str]: - """The names of the supported operations.""" - return _operations + def C_DTYPE(self): + """State vector complex data type.""" + return self._c_dtype + + @property + def num_wires(self): + """State vector complex data type.""" + return self._statevector.num_wires @property - def observables(self) -> frozenset[str]: - """The names of the supported observables.""" - return _observables + def state(self): + """Returns a copy of the state vector data in a NumPy array.""" + return self._statevector.state def _setup_execution_config(self, config): """ diff --git a/pennylane_lightning/lightning_qubit2 b/pennylane_lightning/lightning_qubit2 new file mode 120000 index 0000000000..0bcda1f466 --- /dev/null +++ b/pennylane_lightning/lightning_qubit2 @@ -0,0 +1 @@ +lightning_qubit \ No newline at end of file diff --git a/setup.py b/setup.py index 977f753559..5e37d8e2b2 100644 --- a/setup.py +++ b/setup.py @@ -186,6 +186,7 @@ def build_extension(self, ext: CMakeExtension): suffix = suffix[0].upper() + suffix[1:] pennylane_plugins = [device_name + " = pennylane_lightning." + backend + ":Lightning" + suffix] +pennylane_plugins.append("lightning.qubit2 = pennylane_lightning.lightning_qubit.lightning_qubit2:LightningQubit2") pkg_suffix = "" if suffix == "Qubit" else "_" + suffix diff --git a/tests/conftest.py b/tests/conftest.py index 96531bf421..cbc0d29dde 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -87,7 +87,7 @@ def n_subsystems(request): # Looking for the device for testing. default_device = "lightning.qubit" -supported_devices = {"lightning.kokkos", "lightning.qubit", "lightning.gpu"} +supported_devices = {"lightning.kokkos", "lightning.qubit", "lightning.qubit2", "lightning.gpu"} supported_devices.update({sb.replace(".", "_") for sb in supported_devices}) @@ -131,6 +131,11 @@ def get_device(): if hasattr(pennylane_lightning, "lightning_gpu_ops"): import pennylane_lightning.lightning_gpu_ops as lightning_ops +elif device_name == "lightning.qubit2": + from pennylane_lightning.lightning_qubit2 import LightningQubit2 as LightningDevice + + if hasattr(pennylane_lightning, "lightning_qubit_ops"): + import pennylane_lightning.lightning_qubit_ops as lightning_ops else: from pennylane_lightning.lightning_qubit import LightningQubit as LightningDevice diff --git a/tests/lightning_qubit/test_measurements_class.py b/tests/lightning_qubit/test_measurements_class.py index 23de856293..332162d9c1 100644 --- a/tests/lightning_qubit/test_measurements_class.py +++ b/tests/lightning_qubit/test_measurements_class.py @@ -19,19 +19,27 @@ import numpy as np import pennylane as qml import pytest -from conftest import LightningDevice # tested device +from conftest import LightningDevice, device_name # tested device from pennylane.devices import DefaultQubit from pennylane.measurements import VarianceMP from scipy.sparse import csr_matrix, random_array -from pennylane_lightning.lightning_qubit import LightningQubit +try: + from pennylane_lightning.lightning_qubit_ops import ( + MeasurementsC64, + MeasurementsC128, + ) +except ImportError: + pass + +from pennylane_lightning.lightning_qubit2 import LightningQubit2 from pennylane_lightning.lightning_qubit._measurements import LightningMeasurements from pennylane_lightning.lightning_qubit._state_vector import LightningStateVector -if not LightningQubit._CPP_BINARY_AVAILABLE: +if not LightningDevice._CPP_BINARY_AVAILABLE: pytest.skip("No binary module found. Skipping.", allow_module_level=True) -if LightningDevice != LightningQubit: +if LightningDevice != LightningQubit2: pytest.skip("Exclusive tests for lightning.qubit. Skipping.", allow_module_level=True) THETA = np.linspace(0.11, 1, 3) @@ -420,6 +428,7 @@ def calculate_reference(tape, lightning_sv): ( [0], [1, 2], + [1, 0], qml.PauliX(0), qml.PauliY(1), qml.PauliZ(2), @@ -545,6 +554,29 @@ def test_double_return_value(self, measurement, obs0_, obs1_, lightning_sv, tol) for r, e in zip(result, expected): assert np.allclose(r, e, max(tol, 1.0e-5)) + @pytest.mark.parametrize( + "cases", + [ + [[0, 1], [1, 0]], + [[1, 0], [0, 1]], + ], + ) + def test_probs_tape_unordered_wires(self, cases, tol): + """Test probs with a circuit on wires=[0] fails for out-of-order wires passed to probs.""" + + x, y, z = [0.5, 0.3, -0.7] + dev = qml.device(device_name, wires=cases[1]) + + def circuit(): + qml.RX(0.4, wires=[0]) + qml.Rot(x, y, z, wires=[0]) + qml.RY(-0.2, wires=[0]) + return qml.probs(wires=cases[0]) + + expected = qml.QNode(circuit, qml.device("default.qubit", wires=cases[1]))() + results = qml.QNode(circuit, dev)() + assert np.allclose(expected, results, tol) + class TestControlledOps: """Tests for controlled operations""" diff --git a/tests/lightning_qubit2/test_expval_2.py b/tests/lightning_qubit2/test_expval_2.py index 1349e02b94..6952c49e95 100644 --- a/tests/lightning_qubit2/test_expval_2.py +++ b/tests/lightning_qubit2/test_expval_2.py @@ -14,14 +14,13 @@ """ Tests for process and execute (expval calculation). """ -import pytest - import numpy as np import pennylane as qml -from pennylane_lightning.lightning_qubit import LightningQubit, LightningQubit2 +import pytest +from conftest import LightningDevice # tested device from pennylane.devices import DefaultQubit -from conftest import LightningDevice # tested device +from pennylane_lightning.lightning_qubit import LightningQubit, LightningQubit2 if LightningDevice != LightningQubit: pytest.skip("Exclusive tests for lightning.qubit. Skipping.", allow_module_level=True) diff --git a/tests/test_adjoint_jacobian.py b/tests/test_adjoint_jacobian.py index ad9db67239..df73ec46ca 100644 --- a/tests/test_adjoint_jacobian.py +++ b/tests/test_adjoint_jacobian.py @@ -26,6 +26,9 @@ from pennylane import qchem, qnode from scipy.stats import unitary_group +if ld._new_API: + pytest.skip("Old API required", allow_module_level=True) + I, X, Y, Z = ( np.eye(2), qml.PauliX.compute_matrix(), diff --git a/tests/test_apply.py b/tests/test_apply.py index 5af7d4101a..8aae0e0553 100644 --- a/tests/test_apply.py +++ b/tests/test_apply.py @@ -30,6 +30,7 @@ from pennylane.wires import Wires +@pytest.mark.skipif(ld._new_API, reason="Old API required") class TestApply: """Tests that operations of certain operations are applied correctly or that the proper errors are raised. @@ -532,6 +533,7 @@ def test_apply_state_vector_lightning_handle(self, qubit_device, tol): class TestExpval: """Tests that expectation values are properly calculated or that the proper errors are raised.""" + @pytest.mark.skipif(ld._new_API, reason="Old API required") @pytest.mark.parametrize( "operation,input,expected_output", [ @@ -566,6 +568,7 @@ def test_expval_single_wire_no_parameters( assert np.isclose(res, expected_output, atol=tol, rtol=0) + @pytest.mark.xfail(ld._new_API, reason="Old API required") def test_expval_estimate(self): """Test that the expectation value is not analytically calculated""" dev = qml.device(device_name, wires=1, shots=3) @@ -584,6 +587,7 @@ def circuit(): class TestVar: """Tests that variances are properly calculated.""" + @pytest.mark.skipif(ld._new_API, reason="Old API required") @pytest.mark.parametrize( "operation,input,expected_output", [ @@ -618,6 +622,7 @@ def test_var_single_wire_no_parameters( assert np.isclose(res, expected_output, atol=tol, rtol=0) + @pytest.mark.xfail(ld._new_API, reason="Old API required") def test_var_estimate(self): """Test that the variance is not analytically calculated""" @@ -637,6 +642,7 @@ def circuit(): class TestSample: """Tests that samples are properly calculated.""" + @pytest.mark.skipif(ld._new_API, reason="Old API required") def test_sample_dimensions(self, qubit_device): """Tests if the samples returned by the sample function have the correct dimensions @@ -670,6 +676,7 @@ def test_sample_dimensions(self, qubit_device): s3 = dev.sample(qml.PauliX(0) @ qml.PauliZ(1)) assert np.array_equal(s3.shape, (17,)) + @pytest.mark.skipif(ld._new_API, reason="Old API required") def test_sample_values(self, qubit_device, tol): """Tests if the samples returned by sample have the correct values @@ -697,6 +704,7 @@ class TestLightningDeviceIntegration: """Integration tests for lightning device. This test ensures it integrates properly with the PennyLane interface, in particular QNode.""" + @pytest.mark.xfail(ld._new_API, reason="Old API required") def test_load_default_qubit_device(self): """Test that the default plugin loads correctly""" @@ -705,6 +713,7 @@ def test_load_default_qubit_device(self): assert dev.shots is None assert dev.short_name == device_name + @pytest.mark.xfail(ld._new_API, reason="Old API required") @pytest.mark.skipif(not ld._CPP_BINARY_AVAILABLE, reason="Lightning binary required") def test_no_backprop(self): """Test that lightning device does not support the backprop @@ -719,6 +728,7 @@ def circuit(): with pytest.raises(qml.QuantumFunctionError): qml.QNode(circuit, dev, diff_method="backprop") + @pytest.mark.xfail(ld._new_API, reason="Old API required") @pytest.mark.skipif(not ld._CPP_BINARY_AVAILABLE, reason="Lightning binary required") def test_best_gets_lightning(self): """Test that the best differentiation method returns lightning @@ -767,6 +777,7 @@ def circuit(x): assert np.isclose(circuit(p), 1, atol=tol, rtol=0) + @pytest.mark.xfail(ld._new_API, reason="Old API required") def test_nonzero_shots(self, tol_stochastic): """Test that the default qubit plugin provides correct result for high shot number""" @@ -804,7 +815,8 @@ def test_supported_gate_single_wire_no_parameters( dev = qubit_device(wires=1) op = getattr(qml.ops, name) - assert dev.supports_operation(name) + if hasattr(dev, "supports_operation"): + assert dev.supports_operation(name) @qml.qnode(dev) def circuit(): @@ -830,7 +842,8 @@ def test_supported_gate_two_wires_no_parameters( dev = qubit_device(wires=2) op = getattr(qml.ops, name) - assert dev.supports_operation(name) + if hasattr(dev, "supports_operation"): + assert dev.supports_operation(name) @qml.qnode(dev) def circuit(): @@ -853,7 +866,8 @@ def test_supported_gate_three_wires_no_parameters( dev = qubit_device(wires=3) op = getattr(qml.ops, name) - assert dev.supports_operation(name) + if hasattr(dev, "supports_operation"): + assert dev.supports_operation(name) @qml.qnode(dev) def circuit(): @@ -880,7 +894,8 @@ def test_supported_state_preparation(self, qubit_device, tol, name, par, expecte dev = qubit_device(wires=2) op = getattr(qml.ops, name) - assert dev.supports_operation(name) + if hasattr(dev, "supports_operation"): + assert dev.supports_operation(name) @qml.qnode(dev) def circuit(): @@ -998,7 +1013,8 @@ def test_supported_gate_single_wire_with_parameters( dev = qubit_device(wires=1) op = getattr(qml.ops, name) - assert dev.supports_operation(name) + if hasattr(dev, "supports_operation"): + assert dev.supports_operation(name) @qml.qnode(dev) def circuit(): @@ -1040,7 +1056,8 @@ def test_supported_gate_two_wires_with_parameters( dev = qubit_device(wires=2) op = getattr(qml.ops, name) - assert dev.supports_operation(name) + if hasattr(dev, "supports_operation"): + assert dev.supports_operation(name) @qml.qnode(dev) def circuit(): @@ -1075,7 +1092,8 @@ def test_supported_observable_single_wire_no_parameters( dev = qubit_device(wires=1) obs = getattr(qml.ops, name) - assert dev.supports_observable(name) + if hasattr(dev, "supports_observable"): + assert dev.supports_observable(name) @qml.qnode(dev) def circuit(): @@ -1100,7 +1118,8 @@ def test_supported_observable_single_wire_with_parameters( dev = qubit_device(wires=1) obs = getattr(qml.ops, name) - assert dev.supports_observable(name) + if hasattr(dev, "supports_observable"): + assert dev.supports_observable(name) @qml.qnode(dev) def circuit(): @@ -1109,6 +1128,7 @@ def circuit(): assert np.isclose(circuit(), expected_output, atol=tol, rtol=0) + @pytest.mark.xfail(ld._new_API, reason="Old API required") def test_multi_samples_return_correlated_results(self, qubit_device): """Tests if the samples returned by the sample function have the correct dimensions @@ -1127,6 +1147,7 @@ def circuit(): assert np.array_equal(outcomes[0], outcomes[1]) @pytest.mark.parametrize("num_wires", [3, 4, 5, 6, 7, 8]) + @pytest.mark.xfail(ld._new_API, reason="Old API required") def test_multi_samples_return_correlated_results_more_wires_than_size_of_observable( self, num_wires ): @@ -1146,6 +1167,7 @@ def circuit(): assert np.array_equal(outcomes[0], outcomes[1]) + @pytest.mark.xfail(ld._new_API, reason="Old API required") def test_snapshot_is_ignored_without_shot(self): """Tests if the Snapshot operator is ignored correctly""" dev = qml.device(device_name, wires=4) @@ -1162,6 +1184,7 @@ def circuit(): assert np.allclose(outcomes, [0.0]) + @pytest.mark.xfail(ld._new_API, reason="Old API required") def test_snapshot_is_ignored_with_shots(self): """Tests if the Snapshot operator is ignored correctly""" dev = qml.device(device_name, wires=4, shots=1000) @@ -1188,10 +1211,13 @@ def circuit(): qml.QuantumPhaseEstimation(qml.matrix(qml.Hadamard)(wires=0), [0], [1]) return qml.probs(wires=[0, 1]) - circuit() + probs = circuit() res_sv = dev.state - res_probs = dev.probability([0, 1]) + if ld._new_API: + res_probs = probs + else: + res_probs = dev.probability([0, 1]) expected_sv = np.array( [ @@ -1210,6 +1236,7 @@ def circuit(): class TestApplyLightningMethod: """Unit tests for the apply_lightning method.""" + @pytest.mark.skipif(ld._new_API, reason="Old API required") def test_apply_identity_skipped(self, mocker, tol): """Test identity operation does not perform additional computations.""" dev = qml.device(device_name, wires=1) diff --git a/tests/test_decomposition.py b/tests/test_decomposition.py index 29a7874871..df9de740e2 100644 --- a/tests/test_decomposition.py +++ b/tests/test_decomposition.py @@ -23,6 +23,7 @@ pytest.skip("No binary module found. Skipping.", allow_module_level=True) +@pytest.mark.skipif(ld._new_API, reason="Old API required") class TestDenseMatrixDecompositionThreshold: """Tests, for QFT and Grover operators, the automatic transition from full matrix to decomposition on calculations.""" diff --git a/tests/test_device.py b/tests/test_device.py index 98691b6fd1..11945e89aa 100644 --- a/tests/test_device.py +++ b/tests/test_device.py @@ -37,7 +37,7 @@ def test_create_device_with_dtype(C): not hasattr(np, "complex256"), reason="Numpy only defines complex256 in Linux-like system" ) def test_create_device_with_unsupported_dtype(): - with pytest.raises(TypeError, match="Unsupported complex Type:"): + with pytest.raises(TypeError, match="Unsupported complex type:"): dev = qml.device(device_name, wires=1, c_dtype=np.complex256) diff --git a/tests/test_expval.py b/tests/test_expval.py index 95a985c362..d1b27f9658 100644 --- a/tests/test_expval.py +++ b/tests/test_expval.py @@ -19,9 +19,10 @@ import numpy as np import pennylane as qml import pytest -from conftest import PHI, THETA, VARPHI, device_name +from conftest import PHI, THETA, VARPHI, LightningDevice, device_name +@pytest.mark.skipif(LightningDevice._new_API, reason="Old API required") @pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI))) class TestExpval: """Test expectation values""" @@ -169,6 +170,8 @@ def test_sprod(self, diff_method, qubit_device): """Test the `SProd` class with lightning qubit.""" dev = qubit_device(wires=2) + if diff_method == "adjoint" and dev.short_name == "lightning.qubit2": + return @qml.qnode(dev, diff_method=diff_method) def circuit(x): @@ -187,6 +190,8 @@ def test_prod(self, diff_method, qubit_device): """Test the `Prod` class with lightning qubit.""" dev = qubit_device(wires=2) + if diff_method == "adjoint" and dev.short_name == "lightning.qubit2": + return @qml.qnode(dev, diff_method=diff_method) def circuit(x): @@ -207,6 +212,8 @@ def test_sum(self, diff_method, qubit_device): """Test the `Sum` class with lightning qubit.""" dev = qubit_device(wires=2) + if diff_method == "adjoint" and dev.short_name == "lightning.qubit2": + return @qml.qnode(dev, diff_method=diff_method) def circuit(x, y): @@ -229,6 +236,8 @@ def test_integration(self, diff_method, qubit_device): obs = qml.sum(qml.s_prod(2.3, qml.PauliZ(0)), -0.5 * qml.prod(qml.PauliY(0), qml.PauliZ(1))) dev = qubit_device(wires=2) + if diff_method == "adjoint" and dev.short_name == "lightning.qubit2": + return @qml.qnode(dev, diff_method=diff_method) def circuit(x, y): @@ -248,6 +257,7 @@ def circuit(x, y): assert qml.math.allclose(g, expected) +@pytest.mark.skipif(LightningDevice._new_API, reason="Old API required") @pytest.mark.parametrize("theta,phi,varphi", list(zip(THETA, PHI, VARPHI))) class TestTensorExpval: """Test tensor expectation values""" diff --git a/tests/test_gates.py b/tests/test_gates.py index 847c3a845a..231993989d 100644 --- a/tests/test_gates.py +++ b/tests/test_gates.py @@ -244,6 +244,7 @@ def output(input): assert np.allclose(unitary, random_unitary_inv) +@pytest.mark.skipif(LightningDevice._new_API, reason="Old API required") @pytest.mark.skipif(not LightningDevice._CPP_BINARY_AVAILABLE, reason="Lightning binary required") @pytest.mark.parametrize( "obs,has_rotation", @@ -320,7 +321,7 @@ def circuit(): @pytest.mark.skipif( - device_name != "lightning.qubit", + device_name not in ("lightning.qubit", "lightning.qubit2"), reason="N-controlled operations only implemented in lightning.qubit.", ) @pytest.mark.parametrize("control_value", [False, True]) @@ -363,7 +364,7 @@ def circuit(): @pytest.mark.skipif( - device_name != "lightning.qubit", + device_name not in ("lightning.qubit", "lightning.qubit2"), reason="N-controlled operations only implemented in lightning.qubit.", ) @pytest.mark.parametrize( @@ -440,7 +441,7 @@ def circuit(): @pytest.mark.skipif( - device_name != "lightning.qubit", + device_name not in ("lightning.qubit", "lightning.qubit2"), reason="N-controlled operations only implemented in lightning.qubit.", ) def test_controlled_qubit_unitary_from_op(tol): @@ -461,7 +462,7 @@ def circuit(x): @pytest.mark.skipif( - device_name != "lightning.qubit", + device_name not in ("lightning.qubit", "lightning.qubit2"), reason="N-controlled operations only implemented in lightning.qubit.", ) @pytest.mark.parametrize("control_wires", range(4)) diff --git a/tests/test_measurements.py b/tests/test_measurements.py index 62960f92fd..ea57a50f8c 100644 --- a/tests/test_measurements.py +++ b/tests/test_measurements.py @@ -27,6 +27,7 @@ pytest.skip("No binary module found. Skipping.", allow_module_level=True) +@pytest.mark.skipif(ld._new_API, reason="Old API required") def test_measurements(): dev = qml.device(device_name, wires=2) m = dev.measurements @@ -54,6 +55,7 @@ class TestProbs: def dev(self, request): return qml.device(device_name, wires=2, c_dtype=request.param) + @pytest.mark.skipif(ld._new_API, reason="Old API required") def test_probs_dtype64(self, dev): """Test if probs changes the state dtype""" _state = dev._asarray( @@ -119,6 +121,7 @@ def circuit(): assert np.allclose(circuit(), cases[1], atol=tol, rtol=0) + @pytest.mark.skipif(ld._new_API, reason="Old API required") @pytest.mark.parametrize( "cases", [ @@ -198,6 +201,7 @@ def circuit(): assert np.allclose(circuit(), cases[1], atol=tol, rtol=0) + @pytest.mark.skipif(ld._new_API, reason="Old API required") @pytest.mark.parametrize( "cases", [ @@ -235,6 +239,7 @@ class TestExpval: def dev(self, request): return qml.device(device_name, wires=2, c_dtype=request.param) + @pytest.mark.skipif(ld._new_API, reason="Old API required") def test_expval_dtype64(self, dev): """Test if expval changes the state dtype""" _state = np.array([1, 0, 0, 0]).astype(dev.C_DTYPE) @@ -349,7 +354,7 @@ def circuit(): qml.RX(0.52, wires=0) return qml.expval(qml.RX(0.742, wires=[0])) - with pytest.raises(qml._device.DeviceError, match="Observable RX not supported"): + with pytest.raises(qml._device.DeviceError, match="Observable RX.*not supported"): circuit() def test_observable_return_type_is_expectation(self, dev): @@ -371,6 +376,7 @@ class TestVar: def dev(self, request): return qml.device(device_name, wires=2, c_dtype=request.param) + @pytest.mark.skipif(ld._new_API, reason="Old API required") def test_var_dtype64(self, dev): """Test if var changes the state dtype""" _state = np.array([1, 0, 0, 0]).astype(np.complex64) @@ -449,7 +455,7 @@ def circuit(): qml.RX(0.52, wires=0) return qml.var(qml.RX(0.742, wires=[0])) - with pytest.raises(qml._device.DeviceError, match="Observable RX not supported"): + with pytest.raises(qml._device.DeviceError, match="Observable RX.*not supported"): circuit() def test_observable_return_type_is_variance(self, dev): @@ -478,13 +484,14 @@ def circuit(): qml.RX(0.52, wires=0) return qml.var(qml.RX(0.742, wires=[0])) - with pytest.raises(qml._device.DeviceError, match="Observable RX not supported"): + with pytest.raises(qml._device.DeviceError, match="Observable RX.*not supported"): circuit() class TestWiresInExpval: """Test different Wires settings in Lightning's expval.""" + @pytest.mark.skipif(ld._new_API, reason="Old API required") @pytest.mark.parametrize( "wires1, wires2", [ @@ -529,6 +536,7 @@ def circuit2(): assert np.allclose(circuit1(), circuit2(), atol=tol) + @pytest.mark.skipif(ld._new_API, reason="Old API required") @pytest.mark.parametrize( "wires1, wires2", [ @@ -585,6 +593,7 @@ def circuit2(): class TestSample: """Tests that samples are properly calculated.""" + @pytest.mark.skipif(ld._new_API, reason="Old API required") @pytest.mark.parametrize( "shots, wires", [ @@ -607,6 +616,7 @@ def test_sample_dimensions(self, qubit_device, shots, wires): s1 = dev.sample(qml.PauliZ(wires=[0])) assert np.array_equal(s1.shape, (dev.shots,)) + @pytest.mark.skipif(ld._new_API, reason="Old API required") def test_sample_values(self, qubit_device, tol): """Tests if the samples returned by sample have the correct values @@ -628,6 +638,7 @@ def test_sample_values(self, qubit_device, tol): class TestWiresInVar: """Test different Wires settings in Lightning's var.""" + @pytest.mark.skipif(ld._new_API, reason="Old API required") @pytest.mark.parametrize( "wires1, wires2", [ diff --git a/tests/test_var.py b/tests/test_var.py index bf0779da6f..fc73a4d748 100644 --- a/tests/test_var.py +++ b/tests/test_var.py @@ -17,7 +17,7 @@ import numpy as np import pennylane as qml import pytest -from conftest import PHI, THETA, VARPHI +from conftest import PHI, THETA, VARPHI, LightningDevice np.random.seed(42) @@ -26,6 +26,7 @@ class TestVar: """Tests for the variance""" + @pytest.mark.skipif(LightningDevice._new_API, reason="Old API required") def test_var(self, theta, phi, qubit_device, tol): """Tests for variance calculation""" dev = qubit_device(wires=3) @@ -71,6 +72,7 @@ def circuit(): assert np.allclose(circ(), circ_def(), tol) +@pytest.mark.skipif(LightningDevice._new_API, reason="Old API required") @pytest.mark.parametrize("theta, phi, varphi", list(zip(THETA, PHI, VARPHI))) class TestTensorVar: """Tests for variance of tensor observables""" diff --git a/tests/test_vjp.py b/tests/test_vjp.py index d53b9b4135..70bd091bf9 100644 --- a/tests/test_vjp.py +++ b/tests/test_vjp.py @@ -25,6 +25,9 @@ if not ld._CPP_BINARY_AVAILABLE: pytest.skip("No binary module found. Skipping.", allow_module_level=True) +if ld._new_API: + pytest.skip("Old API required", allow_module_level=True) + class TestVectorJacobianProduct: """Tests for the `vjp` function""" From 2747a84a7aa1e1989cdd1a9fb1a238332446da5d Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Thu, 7 Mar 2024 11:49:29 -0500 Subject: [PATCH 154/428] fix processing_fn_expval --- pennylane_lightning/lightning_qubit/_adjoint_jacobian.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pennylane_lightning/lightning_qubit/_adjoint_jacobian.py b/pennylane_lightning/lightning_qubit/_adjoint_jacobian.py index 98687b5759..04bdc058e2 100644 --- a/pennylane_lightning/lightning_qubit/_adjoint_jacobian.py +++ b/pennylane_lightning/lightning_qubit/_adjoint_jacobian.py @@ -321,6 +321,7 @@ def calculate_vjp(self, tape: QuantumTape, grad_vec): ham = qml.Hamiltonian(grad_vec, [m.obs for m in measurements]) + # pylint: disable=protected-access def processing_fn_expval(tape): nonlocal ham num_params = len(tape.trainable_params) @@ -329,7 +330,7 @@ def processing_fn_expval(tape): return np.array([], dtype=self.qubit_state.dtype) new_tape = tape.copy() - new_tape.measurements = [qml.expval(ham)] + new_tape._measurements = [qml.expval(ham)] return self.calculate_jacobian(new_tape) From 4959e771b0be6f754fdd93323c59395e3878e66c Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Thu, 7 Mar 2024 11:56:27 -0500 Subject: [PATCH 155/428] make a proper new_tape --- pennylane_lightning/lightning_qubit/_adjoint_jacobian.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/_adjoint_jacobian.py b/pennylane_lightning/lightning_qubit/_adjoint_jacobian.py index 04bdc058e2..1c43ef3915 100644 --- a/pennylane_lightning/lightning_qubit/_adjoint_jacobian.py +++ b/pennylane_lightning/lightning_qubit/_adjoint_jacobian.py @@ -321,7 +321,6 @@ def calculate_vjp(self, tape: QuantumTape, grad_vec): ham = qml.Hamiltonian(grad_vec, [m.obs for m in measurements]) - # pylint: disable=protected-access def processing_fn_expval(tape): nonlocal ham num_params = len(tape.trainable_params) @@ -329,8 +328,12 @@ def processing_fn_expval(tape): if num_params == 0: return np.array([], dtype=self.qubit_state.dtype) - new_tape = tape.copy() - new_tape._measurements = [qml.expval(ham)] + new_tape = qml.tape.QuantumScript( + tape.operations, + [qml.expval(ham)], + shots=tape.shots, + trainable_params=tape.trainable_params, + ) return self.calculate_jacobian(new_tape) From 0c306b8bbde646b8be6d264ba223ab0a5b054a36 Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Thu, 7 Mar 2024 12:13:53 -0500 Subject: [PATCH 156/428] Added init tests; Added skeleton tests for helpers --- .../lightning_qubit2/test_lightning_qubit2.py | 110 +++++++++++++++++- 1 file changed, 109 insertions(+), 1 deletion(-) diff --git a/tests/lightning_qubit2/test_lightning_qubit2.py b/tests/lightning_qubit2/test_lightning_qubit2.py index 4b040ce2e3..30e1a941dd 100644 --- a/tests/lightning_qubit2/test_lightning_qubit2.py +++ b/tests/lightning_qubit2/test_lightning_qubit2.py @@ -21,11 +21,16 @@ import pennylane as qml from pennylane_lightning.lightning_qubit import LightningQubit, LightningQubit2 from pennylane_lightning.lightning_qubit.lightning_qubit2 import ( - simulate, + accepted_observables, jacobian, + simulate, simulate_and_jacobian, + stopping_condition, ) +from pennylane_lightning.lightning_qubit._state_vector import LightningStateVector +from pennylane_lightning.lightning_qubit._measurements import LightningMeasurements from pennylane.devices import DefaultQubit +from pennylane.tape import QuantumScript from conftest import LightningDevice # tested device @@ -49,12 +54,115 @@ class TestHelpers: # Test stopping_condition # Test accepted_observables + @pytest.fixture(params=[np.complex64, np.complex128]) + def dev(self, request): + return LightningQubit2(wires=3, c_dtype=request.param) + + @staticmethod + def calculate_reference(tape): + dev = DefaultQubit(max_workers=1) + program, _ = dev.preprocess() + tapes, transf_fn = program([tape]) + results = dev.execute(tapes) + return transf_fn(results) + + @staticmethod + def process_and_execute(dev, tape): + program, _ = dev.preprocess() + tapes, transf_fn = program([tape]) + results = dev.execute(tapes) + return transf_fn(results) + + class DummyOperator(qml.operation.Operation, qml.operation.Observable): + """Dummy operator""" + + num_wires = 1 + + def test_stopping_condition(self): + """Test that stopping_condition returns whether or not an operation + is supported by the device.""" + valid_op = qml.RX(1.23, 0) + invalid_op = self.DummyOperator(0) + + assert stopping_condition(valid_op) is True + assert stopping_condition(invalid_op) is False + + def test_accepted_observables(self): + """Test that accepted_observables returns whether or not an observable + is supported by the device.""" + valid_obs = qml.Projector([0], 0) + invalid_obs = self.DummyOperator(0) + + assert accepted_observables(valid_obs) is True + assert accepted_observables(invalid_obs) is False + + @pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI))) + def test_simulate_single_measurement(self, theta, phi, dev): + """Test that simulate returns the correct results with a single measurement.""" + return + + @pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI))) + def test_simulate_multi_measurement(self, theta, phi, dev): + """Test that simulate returns the correct results with multiple measurements.""" + return + + @pytest.mark.parametrize("theta", THETA) + def test_jacobian_returns_zero(self, theta): + """Test that jacobian always returns zero.""" + tape = QuantumScript([qml.RX(theta, wires=0)], [qml.expval(qml.Z(0))]) + assert np.allclose(jacobian(tape), 0) + + @pytest.mark.xfail + @pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI))) + def test_jacobian_single_expval(self, theta, phi, dev): + """Test that the jacobian is correct when a tape has a single expectation value""" + return + + @pytest.mark.xfail + @pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI))) + def test_jacobian_multi_expval(self, theta, phi, dev): + """Test that the jacobian is correct when a tape has multiple expectation values""" + return + + @pytest.mark.xfail + @pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI))) + def test_simulate_and_jacobian_single_expval(self, theta, phi, dev): + """Test that the result and jacobian is correct when a tape has a single + expectation value""" + return + + @pytest.mark.xfail + @pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI))) + def test_simulate_and_jacobian_multi_expval(self, theta, phi, dev): + """Test that the result and jacobian is correct when a tape has multiple + expectation values""" + return + class TestInitialization: """Unit tests for LightningQubit2 initialization""" # Test __init__ errors: invalid num_burnin, kernel name + def test_invalid_num_burnin_error(self): + """Test that an error is raised when num_burnin is more than number of shots""" + n_shots = 10 + num_burnin = 11 + + with pytest.raises(ValueError, match="Shots should be greater than num_burnin."): + _ = LightningQubit2(wires=2, shots=n_shots, mcmc=True, num_burnin=num_burnin) + + def test_invalid_kernel_name(self): + """Test that an error is raised when the kernel_name is not "Local" or "NonZeroRandom".""" + + _ = LightningQubit2(wires=2, shots=1000, mcmc=True, kernel_name="Local") + _ = LightningQubit2(wires=2, shots=1000, mcmc=True, kernel_name="NonZeroRandom") + + with pytest.raises( + NotImplementedError, match="only 'Local' and 'NonZeroRandom' kernels are supported" + ): + _ = LightningQubit2(wires=2, shots=1000, mcmc=True, kernel_name="bleh") + class TestExecution: """Unit tests for executing quantum tapes on LightningQubit2""" From 9e6cc024686d6b7b30be26bfd9f52dcc466cfc14 Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Thu, 7 Mar 2024 17:28:55 +0000 Subject: [PATCH 157/428] Fix more bug with shots. --- .../lightning_gpu/lightning_gpu.py | 27 ++++++---- .../lightning_kokkos/lightning_kokkos.py | 53 ++++++++++++------- .../lightning_qubit/lightning_qubit.py | 20 ++++--- tests/test_measurements.py | 13 +++-- 4 files changed, 76 insertions(+), 37 deletions(-) diff --git a/pennylane_lightning/lightning_gpu/lightning_gpu.py b/pennylane_lightning/lightning_gpu/lightning_gpu.py index 1a5a45798b..d2345b1360 100644 --- a/pennylane_lightning/lightning_gpu/lightning_gpu.py +++ b/pennylane_lightning/lightning_gpu/lightning_gpu.py @@ -91,6 +91,13 @@ from pennylane.ops.op_math import Adjoint from pennylane.wires import Wires + # pylint: disable=import-error, no-name-in-module, ungrouped-imports + from pennylane_lightning.core._serialize import ( + QuantumScriptSerializer, + global_phase_diagonal, + ) + from pennylane_lightning.core._version import __version__ + # pylint: disable=no-name-in-module, ungrouped-imports from pennylane_lightning.lightning_gpu_ops.algorithms import ( AdjointJacobianC64, @@ -99,13 +106,6 @@ create_ops_listC128, ) - # pylint: disable=import-error, no-name-in-module, ungrouped-imports - from pennylane_lightning.core._serialize import ( - QuantumScriptSerializer, - global_phase_diagonal, - ) - from pennylane_lightning.core._version import __version__ - if MPI_SUPPORT: from pennylane_lightning.lightning_gpu_ops.algorithmsMPI import ( AdjointJacobianMPIC64, @@ -852,12 +852,19 @@ def processing_fn(tape): # pylint: disable=attribute-defined-outside-init def sample(self, observable, shot_range=None, bin_size=None, counts=False): """Return samples of an observable.""" - if observable.name != "PauliZ": - self.apply_lightning(observable.diagonalizing_gates()) + diagonalizing_gates = observable.diagonalizing_gates() + if diagonalizing_gates: + self.apply_lightning(diagonalizing_gates) + if not isinstance(observable, qml.PauliZ): self._samples = self.generate_samples() - return super().sample( + results = super().sample( observable, shot_range=shot_range, bin_size=bin_size, counts=counts ) + if diagonalizing_gates: + self.apply_lightning( + [qml.adjoint(g, lazy=False) for g in reversed(diagonalizing_gates)] + ) + return results # pylint: disable=missing-function-docstring def generate_samples(self): diff --git a/pennylane_lightning/lightning_kokkos/lightning_kokkos.py b/pennylane_lightning/lightning_kokkos/lightning_kokkos.py index 9953e2651c..25e3de26c5 100644 --- a/pennylane_lightning/lightning_kokkos/lightning_kokkos.py +++ b/pennylane_lightning/lightning_kokkos/lightning_kokkos.py @@ -64,6 +64,13 @@ from pennylane.ops.op_math import Adjoint from pennylane.wires import Wires + # pylint: disable=import-error, no-name-in-module, ungrouped-imports + from pennylane_lightning.core._serialize import ( + QuantumScriptSerializer, + global_phase_diagonal, + ) + from pennylane_lightning.core._version import __version__ + # pylint: disable=import-error, no-name-in-module, ungrouped-imports from pennylane_lightning.lightning_kokkos_ops.algorithms import ( AdjointJacobianC64, @@ -72,13 +79,6 @@ create_ops_listC128, ) - # pylint: disable=import-error, no-name-in-module, ungrouped-imports - from pennylane_lightning.core._serialize import ( - QuantumScriptSerializer, - global_phase_diagonal, - ) - from pennylane_lightning.core._version import __version__ - def _kokkos_dtype(dtype): if dtype not in [np.complex128, np.complex64]: # pragma: no cover raise ValueError(f"Data type is not supported for state-vector computation: {dtype}") @@ -475,10 +475,15 @@ def expval(self, observable, shot_range=None, bin_size=None): if observable.name in [ "Projector", ]: - if self.shots is None: - qs = qml.tape.QuantumScript([], [qml.expval(observable)]) - self.apply(self._get_diagonalizing_gates(qs)) - return super().expval(observable, shot_range=shot_range, bin_size=bin_size) + diagonalizing_gates = observable.diagonalizing_gates() + if self.shots is None and diagonalizing_gates: + self.apply_lightning(diagonalizing_gates) + results = super().expval(observable, shot_range=shot_range, bin_size=bin_size) + if diagonalizing_gates: + self.apply_lightning( + [qml.adjoint(g, lazy=False) for g in reversed(diagonalizing_gates)] + ) + return results if self.shots is not None: # estimate the expectation value @@ -538,10 +543,15 @@ def var(self, observable, shot_range=None, bin_size=None): if observable.name in [ "Projector", ]: - if self.shots is None: - qs = qml.tape.QuantumScript([], [qml.var(observable)]) - self.apply(self._get_diagonalizing_gates(qs)) - return super().var(observable, shot_range=shot_range, bin_size=bin_size) + diagonalizing_gates = observable.diagonalizing_gates() + if self.shots is None and diagonalizing_gates: + self.apply_lightning(diagonalizing_gates) + results = super().var(observable, shot_range=shot_range, bin_size=bin_size) + if diagonalizing_gates: + self.apply_lightning( + [qml.adjoint(g, lazy=False) for g in reversed(diagonalizing_gates)] + ) + return results if self.shots is not None: # estimate the var @@ -608,12 +618,19 @@ def probability_lightning(self, wires): # pylint: disable=attribute-defined-outside-init def sample(self, observable, shot_range=None, bin_size=None, counts=False): """Return samples of an observable.""" - if observable.name != "PauliZ": - self.apply_lightning(observable.diagonalizing_gates()) + diagonalizing_gates = observable.diagonalizing_gates() + if diagonalizing_gates: + self.apply_lightning(diagonalizing_gates) + if not isinstance(observable, qml.PauliZ): self._samples = self.generate_samples() - return super().sample( + results = super().sample( observable, shot_range=shot_range, bin_size=bin_size, counts=counts ) + if diagonalizing_gates: + self.apply_lightning( + [qml.adjoint(g, lazy=False) for g in reversed(diagonalizing_gates)] + ) + return results @staticmethod def _check_adjdiff_supported_measurements( diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit.py b/pennylane_lightning/lightning_qubit/lightning_qubit.py index 79c1e319f8..cc5acfc473 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit.py @@ -501,7 +501,9 @@ def expval(self, observable, shot_range=None, bin_size=None): self.apply_lightning(diagonalizing_gates) results = super().expval(observable, shot_range=shot_range, bin_size=bin_size) if diagonalizing_gates: - self.apply_lightning([qml.adjoint(g, lazy=False) for g in diagonalizing_gates]) + self.apply_lightning( + [qml.adjoint(g, lazy=False) for g in reversed(diagonalizing_gates)] + ) return results if self.shots is not None: @@ -560,7 +562,9 @@ def var(self, observable, shot_range=None, bin_size=None): self.apply_lightning(diagonalizing_gates) results = super().var(observable, shot_range=shot_range, bin_size=bin_size) if diagonalizing_gates: - self.apply_lightning([qml.adjoint(g, lazy=False) for g in diagonalizing_gates]) + self.apply_lightning( + [qml.adjoint(g, lazy=False) for g in reversed(diagonalizing_gates)] + ) return results if self.shots is not None: @@ -640,12 +644,16 @@ def sample(self, observable, shot_range=None, bin_size=None, counts=False): diagonalizing_gates = observable.diagonalizing_gates() if diagonalizing_gates: self.apply_lightning(diagonalizing_gates) - self._samples = self.generate_samples() - if diagonalizing_gates: - self.apply_lightning([qml.adjoint(g, lazy=False) for g in diagonalizing_gates]) - return super().sample( + if not isinstance(observable, qml.PauliZ): + self._samples = self.generate_samples() + results = super().sample( observable, shot_range=shot_range, bin_size=bin_size, counts=counts ) + if diagonalizing_gates: + self.apply_lightning( + [qml.adjoint(g, lazy=False) for g in reversed(diagonalizing_gates)] + ) + return results @staticmethod def _check_adjdiff_supported_measurements( diff --git a/tests/test_measurements.py b/tests/test_measurements.py index 3cf25b300e..7d9509b6bc 100644 --- a/tests/test_measurements.py +++ b/tests/test_measurements.py @@ -700,16 +700,23 @@ def test_shots_single_measure_obs(shots, measure_f, obs, mcmc, kernel_name): """Tests that Lightning handles shots in a circuit where a single measurement of a common observable is performed at the end.""" n_qubits = 2 + if device_name in ("lightning.gpu", "lightning.kokkos") and (mcmc or kernel_name != "Local"): + pytest.skip(f"Device {device_name} does not have an mcmc option.") + if measure_f in (qml.expval, qml.var) and isinstance(obs, Sequence): pytest.skip("qml.expval, qml.var do not take wire arguments.") - dev = qml.device(device_name, wires=n_qubits, shots=shots, mcmc=mcmc, kernel_name=kernel_name) + if device_name in ("lightning.gpu", "lightning.kokkos"): + dev = qml.device(device_name, wires=n_qubits, shots=shots) + else: + dev = qml.device( + device_name, wires=n_qubits, shots=shots, mcmc=mcmc, kernel_name=kernel_name + ) dq = qml.device("default.qubit", wires=n_qubits, shots=shots) params = [np.pi / 4, -np.pi / 4] def func(x, y): - for i in range(n_qubits): - qml.Hadamard(i) + qml.Hadamard(0) qml.RX(x, 0) qml.RX(y, 1) return measure_f(wires=obs) if isinstance(obs, Sequence) else measure_f(op=obs) From 75c116aa6907a5c451e86ac9e2ea93c178f48feb Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Thu, 7 Mar 2024 13:29:59 -0500 Subject: [PATCH 158/428] trigger CI From 84d72393f67c3d4433ff640f2e53614245a34d2e Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Thu, 7 Mar 2024 19:25:28 +0000 Subject: [PATCH 159/428] Change pennylane branch for CI. --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index aee5243438..a09ea5e643 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,5 +1,5 @@ pip~=22.0 -git+https://github.com/PennyLaneAI/pennylane.git@master +git+https://github.com/PennyLaneAI/pennylane.git@qnode-let-device-interpret-best ninja flaky pybind11 From e3bc2416fb60e8542d8dd53524cfbf0ef5bccb67 Mon Sep 17 00:00:00 2001 From: Amintor Dusko <87949283+AmintorDusko@users.noreply.github.com> Date: Thu, 7 Mar 2024 14:56:31 -0500 Subject: [PATCH 160/428] Update .github/CHANGELOG.md Co-authored-by: Vincent Michaud-Rioux --- .github/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index 60c1fa173d..effecdcc99 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -2,7 +2,7 @@ ### New features since last release -* Add two new python classes (LightningStateVector and LightningMeasurements)to support `lightning.qubit2`. +* Add two new python classes (LightningStateVector and LightningMeasurements) to support `lightning.qubit2`. [(#613)](https://github.com/PennyLaneAI/pennylane-lightning/pull/613) * Add analytic-mode `qml.probs` and `qml.var` support in `lightning.qubit2`. From ef35f9d3792725689f7773cab72ec2c69bdbb766 Mon Sep 17 00:00:00 2001 From: Amintor Dusko <87949283+AmintorDusko@users.noreply.github.com> Date: Thu, 7 Mar 2024 14:56:36 -0500 Subject: [PATCH 161/428] Update pennylane_lightning/lightning_qubit/_adjoint_jacobian.py Co-authored-by: Vincent Michaud-Rioux --- pennylane_lightning/lightning_qubit/_adjoint_jacobian.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pennylane_lightning/lightning_qubit/_adjoint_jacobian.py b/pennylane_lightning/lightning_qubit/_adjoint_jacobian.py index 1c43ef3915..986903b735 100644 --- a/pennylane_lightning/lightning_qubit/_adjoint_jacobian.py +++ b/pennylane_lightning/lightning_qubit/_adjoint_jacobian.py @@ -132,7 +132,6 @@ def _check_adjdiff_supported_operations(operations): 'the "adjoint" differentiation method' ) - # pylint: disable=too-many-function-args, assignment-from-no-return, too-many-arguments def _process_jacobian_tape( self, tape: QuantumTape, use_mpi: bool = False, split_obs: bool = False ): From e159b55b495bb0f8ee33d533c3dac8a3a1f22f2d Mon Sep 17 00:00:00 2001 From: Amintor Dusko <87949283+AmintorDusko@users.noreply.github.com> Date: Thu, 7 Mar 2024 14:56:41 -0500 Subject: [PATCH 162/428] Update pennylane_lightning/lightning_qubit/_adjoint_jacobian.py Co-authored-by: Vincent Michaud-Rioux --- pennylane_lightning/lightning_qubit/_adjoint_jacobian.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/lightning_qubit/_adjoint_jacobian.py b/pennylane_lightning/lightning_qubit/_adjoint_jacobian.py index 986903b735..e633c13cc5 100644 --- a/pennylane_lightning/lightning_qubit/_adjoint_jacobian.py +++ b/pennylane_lightning/lightning_qubit/_adjoint_jacobian.py @@ -261,7 +261,7 @@ def calculate_jacobian(self, tape: QuantumTape): return self._adjoint_jacobian_processing(jac_r) if qml.active_return() else jac_r return self._adjoint_jacobian_processing(jac_r) - # pylint: disable=line-too-long, inconsistent-return-statements + # pylint: disable=inconsistent-return-statements def calculate_vjp(self, tape: QuantumTape, grad_vec): """Generate the processing function required to compute the vector-Jacobian products of a tape. From 4363687d15099ad17af2762805334d2707ded4f1 Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Tue, 5 Mar 2024 01:25:02 +0000 Subject: [PATCH 163/428] Add probs support. --- .../test_measurements_class.py | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/tests/lightning_qubit/test_measurements_class.py b/tests/lightning_qubit/test_measurements_class.py index 23de856293..8cb8daefc9 100644 --- a/tests/lightning_qubit/test_measurements_class.py +++ b/tests/lightning_qubit/test_measurements_class.py @@ -16,6 +16,7 @@ import math from typing import Sequence +from typing import Sequence import numpy as np import pennylane as qml import pytest @@ -545,6 +546,74 @@ def test_double_return_value(self, measurement, obs0_, obs1_, lightning_sv, tol) for r, e in zip(result, expected): assert np.allclose(r, e, max(tol, 1.0e-5)) + @pytest.mark.parametrize("measurement", [qml.expval, qml.probs, qml.var]) + @pytest.mark.parametrize( + "obs0_", + ( + qml.PauliX(0), + qml.PauliY(1), + qml.PauliZ(2), + qml.sum(qml.PauliX(0), qml.PauliY(0)), + qml.prod(qml.PauliX(0), qml.PauliY(1)), + qml.s_prod(2.0, qml.PauliX(0)), + qml.Hermitian(get_hermitian_matrix(2), wires=[0]), + qml.Hermitian(get_hermitian_matrix(2**2), wires=[2, 3]), + qml.Hamiltonian( + [1.0, 2.0, 3.0], [qml.PauliX(0), qml.PauliY(1), qml.PauliZ(2) @ qml.PauliZ(3)] + ), + qml.SparseHamiltonian(get_sparse_hermitian_matrix(2**4), wires=range(4)), + ), + ) + @pytest.mark.parametrize( + "obs1_", + ( + qml.PauliX(0), + qml.PauliY(1), + qml.PauliZ(2), + qml.sum(qml.PauliX(0), qml.PauliY(0)), + qml.prod(qml.PauliX(0), qml.PauliY(1)), + qml.s_prod(2.0, qml.PauliX(0)), + qml.Hermitian(get_hermitian_matrix(2), wires=[0]), + qml.Hermitian(get_hermitian_matrix(2**2), wires=[2, 3]), + # qml.Hamiltonian( + # [1.0, 2.0, 3.0], [qml.PauliX(0), qml.PauliY(1), qml.PauliZ(2) @ qml.PauliZ(3)] + # ), + # qml.SparseHamiltonian(get_sparse_hermitian_matrix(2**4), wires=range(4)), + ), + ) + def test_double_return_value(self, measurement, obs0_, obs1_, lightning_sv, tol): + if measurement is qml.probs and isinstance( + obs0_, + (qml.ops.Sum, qml.ops.SProd, qml.ops.Prod, qml.Hamiltonian, qml.SparseHamiltonian), + ): + return + if measurement is qml.probs and isinstance( + obs1_, + (qml.ops.Sum, qml.ops.SProd, qml.ops.Prod, qml.Hamiltonian, qml.SparseHamiltonian), + ): + return + n_qubits = 4 + n_layers = 1 + np.random.seed(0) + weights = np.random.rand(n_layers, n_qubits, 3) + ops = [qml.Hadamard(i) for i in range(n_qubits)] + ops += [qml.StronglyEntanglingLayers(weights, wires=range(n_qubits))] + measurements = [measurement(op=obs0_), measurement(op=obs1_)] + tape = qml.tape.QuantumScript(ops, measurements) + + expected = self.calculate_reference(tape, lightning_sv) + if len(expected) == 1: + expected = expected[0] + statevector = lightning_sv(n_qubits) + statevector = statevector.get_final_state(tape) + m = LightningMeasurements(statevector) + result = m.measure_final_state(tape) + + assert isinstance(result, Sequence) + assert len(result) == len(expected) + for r, e in zip(result, expected): + assert np.allclose(r, e, rtol=1.0e-5, atol=0.0) + class TestControlledOps: """Tests for controlled operations""" From c1d2ee6216ce27b379c10428e789fdc2ee61fbcc Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Tue, 5 Mar 2024 15:24:11 +0000 Subject: [PATCH 164/428] Add double-obs tests. --- tests/lightning_qubit/test_measurements_class.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/lightning_qubit/test_measurements_class.py b/tests/lightning_qubit/test_measurements_class.py index 8cb8daefc9..35a8d0a261 100644 --- a/tests/lightning_qubit/test_measurements_class.py +++ b/tests/lightning_qubit/test_measurements_class.py @@ -575,10 +575,10 @@ def test_double_return_value(self, measurement, obs0_, obs1_, lightning_sv, tol) qml.s_prod(2.0, qml.PauliX(0)), qml.Hermitian(get_hermitian_matrix(2), wires=[0]), qml.Hermitian(get_hermitian_matrix(2**2), wires=[2, 3]), - # qml.Hamiltonian( - # [1.0, 2.0, 3.0], [qml.PauliX(0), qml.PauliY(1), qml.PauliZ(2) @ qml.PauliZ(3)] - # ), - # qml.SparseHamiltonian(get_sparse_hermitian_matrix(2**4), wires=range(4)), + qml.Hamiltonian( + [1.0, 2.0, 3.0], [qml.PauliX(0), qml.PauliY(1), qml.PauliZ(2) @ qml.PauliZ(3)] + ), + qml.SparseHamiltonian(get_sparse_hermitian_matrix(2**4), wires=range(4)), ), ) def test_double_return_value(self, measurement, obs0_, obs1_, lightning_sv, tol): @@ -611,8 +611,9 @@ def test_double_return_value(self, measurement, obs0_, obs1_, lightning_sv, tol) assert isinstance(result, Sequence) assert len(result) == len(expected) + # a few tests fail in single precision, and hence we increase the tolerance for r, e in zip(result, expected): - assert np.allclose(r, e, rtol=1.0e-5, atol=0.0) + assert np.allclose(r, e, max(tol, 1.0e-5)) class TestControlledOps: From 5535a6814a3e7605031d8f18eebd39043a19e806 Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Mon, 4 Mar 2024 19:48:31 +0000 Subject: [PATCH 165/428] Add qml.var support. --- pennylane_lightning/lightning_qubit/_measurements.py | 3 +++ tests/lightning_qubit/test_measurements_class.py | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/pennylane_lightning/lightning_qubit/_measurements.py b/pennylane_lightning/lightning_qubit/_measurements.py index 4a81b1c8e9..baa2b2d0f1 100644 --- a/pennylane_lightning/lightning_qubit/_measurements.py +++ b/pennylane_lightning/lightning_qubit/_measurements.py @@ -212,9 +212,12 @@ def get_measurement_function( return self.state_diagonalizing_gates return self.expval +<<<<<<< HEAD if isinstance(measurementprocess, ProbabilityMP): return self.probs +======= +>>>>>>> c1c03954 (Add qml.var support.) if isinstance(measurementprocess, VarianceMP): if measurementprocess.obs.name in [ "Identity", diff --git a/tests/lightning_qubit/test_measurements_class.py b/tests/lightning_qubit/test_measurements_class.py index 35a8d0a261..450dcbe58b 100644 --- a/tests/lightning_qubit/test_measurements_class.py +++ b/tests/lightning_qubit/test_measurements_class.py @@ -93,6 +93,10 @@ def test_only_support_state_measurements(self, lightning_sv): @pytest.mark.parametrize( "mp", ( +<<<<<<< HEAD +======= + qml.probs(wires=0), +>>>>>>> c1c03954 (Add qml.var support.) qml.vn_entropy(wires=0), CustomStateMeasurement(), qml.expval(qml.Identity(0)), From 9f5e70f4ed8e1574b24c6d93c958dd451b2ee1cd Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Tue, 5 Mar 2024 01:25:02 +0000 Subject: [PATCH 166/428] Add probs support. --- pennylane_lightning/lightning_qubit/_measurements.py | 6 ++++++ tests/lightning_qubit/test_measurements_class.py | 4 ---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/_measurements.py b/pennylane_lightning/lightning_qubit/_measurements.py index baa2b2d0f1..77b9a58466 100644 --- a/pennylane_lightning/lightning_qubit/_measurements.py +++ b/pennylane_lightning/lightning_qubit/_measurements.py @@ -212,12 +212,18 @@ def get_measurement_function( return self.state_diagonalizing_gates return self.expval +<<<<<<< HEAD <<<<<<< HEAD if isinstance(measurementprocess, ProbabilityMP): return self.probs ======= >>>>>>> c1c03954 (Add qml.var support.) +======= + if isinstance(measurementprocess, ProbabilityMP): + return self.probs + +>>>>>>> 383efdb1 (Add probs support.) if isinstance(measurementprocess, VarianceMP): if measurementprocess.obs.name in [ "Identity", diff --git a/tests/lightning_qubit/test_measurements_class.py b/tests/lightning_qubit/test_measurements_class.py index 450dcbe58b..35a8d0a261 100644 --- a/tests/lightning_qubit/test_measurements_class.py +++ b/tests/lightning_qubit/test_measurements_class.py @@ -93,10 +93,6 @@ def test_only_support_state_measurements(self, lightning_sv): @pytest.mark.parametrize( "mp", ( -<<<<<<< HEAD -======= - qml.probs(wires=0), ->>>>>>> c1c03954 (Add qml.var support.) qml.vn_entropy(wires=0), CustomStateMeasurement(), qml.expval(qml.Identity(0)), From ef33d29ef648fdeb0c44cbd520b132d0bb650a3d Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Tue, 5 Mar 2024 15:34:22 +0000 Subject: [PATCH 167/428] Add measurement tests with wires. --- pennylane_lightning/lightning_gpu/lightning_gpu.py | 14 +++++++------- .../lightning_qubit/_measurements.py | 9 --------- tests/lightning_qubit/test_measurements_class.py | 3 ++- 3 files changed, 9 insertions(+), 17 deletions(-) diff --git a/pennylane_lightning/lightning_gpu/lightning_gpu.py b/pennylane_lightning/lightning_gpu/lightning_gpu.py index be36ff548d..1ff0642620 100644 --- a/pennylane_lightning/lightning_gpu/lightning_gpu.py +++ b/pennylane_lightning/lightning_gpu/lightning_gpu.py @@ -91,6 +91,13 @@ from pennylane.ops.op_math import Adjoint from pennylane.wires import Wires + # pylint: disable=import-error, no-name-in-module, ungrouped-imports + from pennylane_lightning.core._serialize import ( + QuantumScriptSerializer, + global_phase_diagonal, + ) + from pennylane_lightning.core._version import __version__ + # pylint: disable=no-name-in-module, ungrouped-imports from pennylane_lightning.lightning_gpu_ops.algorithms import ( AdjointJacobianC64, @@ -99,13 +106,6 @@ create_ops_listC128, ) - # pylint: disable=import-error, no-name-in-module, ungrouped-imports - from pennylane_lightning.core._serialize import ( - QuantumScriptSerializer, - global_phase_diagonal, - ) - from pennylane_lightning.core._version import __version__ - if MPI_SUPPORT: from pennylane_lightning.lightning_gpu_ops.algorithmsMPI import ( AdjointJacobianMPIC64, diff --git a/pennylane_lightning/lightning_qubit/_measurements.py b/pennylane_lightning/lightning_qubit/_measurements.py index 77b9a58466..4a81b1c8e9 100644 --- a/pennylane_lightning/lightning_qubit/_measurements.py +++ b/pennylane_lightning/lightning_qubit/_measurements.py @@ -212,18 +212,9 @@ def get_measurement_function( return self.state_diagonalizing_gates return self.expval -<<<<<<< HEAD -<<<<<<< HEAD if isinstance(measurementprocess, ProbabilityMP): return self.probs -======= ->>>>>>> c1c03954 (Add qml.var support.) -======= - if isinstance(measurementprocess, ProbabilityMP): - return self.probs - ->>>>>>> 383efdb1 (Add probs support.) if isinstance(measurementprocess, VarianceMP): if measurementprocess.obs.name in [ "Identity", diff --git a/tests/lightning_qubit/test_measurements_class.py b/tests/lightning_qubit/test_measurements_class.py index 35a8d0a261..770a4d75d2 100644 --- a/tests/lightning_qubit/test_measurements_class.py +++ b/tests/lightning_qubit/test_measurements_class.py @@ -17,6 +17,7 @@ from typing import Sequence from typing import Sequence + import numpy as np import pennylane as qml import pytest @@ -611,7 +612,7 @@ def test_double_return_value(self, measurement, obs0_, obs1_, lightning_sv, tol) assert isinstance(result, Sequence) assert len(result) == len(expected) - # a few tests fail in single precision, and hence we increase the tolerance + # a few tests may fail in single precision, and hence we increase the tolerance for r, e in zip(result, expected): assert np.allclose(r, e, max(tol, 1.0e-5)) From 1fdab8ca60e46125ba2e43117ca91c7c325ea983 Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Tue, 5 Mar 2024 22:26:01 +0000 Subject: [PATCH 168/428] pytest.skip tests --- pennylane_lightning/lightning_gpu/lightning_gpu.py | 14 +++++++------- .../lightning_kokkos/lightning_kokkos.py | 6 ++++++ tests/lightning_qubit/test_measurements_class.py | 5 ++++- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/pennylane_lightning/lightning_gpu/lightning_gpu.py b/pennylane_lightning/lightning_gpu/lightning_gpu.py index 1ff0642620..be36ff548d 100644 --- a/pennylane_lightning/lightning_gpu/lightning_gpu.py +++ b/pennylane_lightning/lightning_gpu/lightning_gpu.py @@ -91,13 +91,6 @@ from pennylane.ops.op_math import Adjoint from pennylane.wires import Wires - # pylint: disable=import-error, no-name-in-module, ungrouped-imports - from pennylane_lightning.core._serialize import ( - QuantumScriptSerializer, - global_phase_diagonal, - ) - from pennylane_lightning.core._version import __version__ - # pylint: disable=no-name-in-module, ungrouped-imports from pennylane_lightning.lightning_gpu_ops.algorithms import ( AdjointJacobianC64, @@ -106,6 +99,13 @@ create_ops_listC128, ) + # pylint: disable=import-error, no-name-in-module, ungrouped-imports + from pennylane_lightning.core._serialize import ( + QuantumScriptSerializer, + global_phase_diagonal, + ) + from pennylane_lightning.core._version import __version__ + if MPI_SUPPORT: from pennylane_lightning.lightning_gpu_ops.algorithmsMPI import ( AdjointJacobianMPIC64, diff --git a/pennylane_lightning/lightning_kokkos/lightning_kokkos.py b/pennylane_lightning/lightning_kokkos/lightning_kokkos.py index 4878431445..de531ba2fe 100644 --- a/pennylane_lightning/lightning_kokkos/lightning_kokkos.py +++ b/pennylane_lightning/lightning_kokkos/lightning_kokkos.py @@ -63,6 +63,12 @@ from pennylane.operation import Tensor from pennylane.ops.op_math import Adjoint from pennylane.wires import Wires + from pennylane_lightning.lightning_kokkos_ops.algorithms import ( + AdjointJacobianC64, + AdjointJacobianC128, + create_ops_listC64, + create_ops_listC128, + ) # pylint: disable=import-error, no-name-in-module, ungrouped-imports from pennylane_lightning.core._serialize import ( diff --git a/tests/lightning_qubit/test_measurements_class.py b/tests/lightning_qubit/test_measurements_class.py index 770a4d75d2..42b589a089 100644 --- a/tests/lightning_qubit/test_measurements_class.py +++ b/tests/lightning_qubit/test_measurements_class.py @@ -592,7 +592,10 @@ def test_double_return_value(self, measurement, obs0_, obs1_, lightning_sv, tol) obs1_, (qml.ops.Sum, qml.ops.SProd, qml.ops.Prod, qml.Hamiltonian, qml.SparseHamiltonian), ): - return + pytest.skip( + f"Observable of type {type(obs1_).__name__} is not supported for rotating probabilities." + ) + n_qubits = 4 n_layers = 1 np.random.seed(0) From 3d9eb6f19cb7cfbb69798c27adb82c53e75eb9cc Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Wed, 6 Mar 2024 13:23:28 +0000 Subject: [PATCH 169/428] Fix format --- pennylane_lightning/lightning_kokkos/lightning_kokkos.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pennylane_lightning/lightning_kokkos/lightning_kokkos.py b/pennylane_lightning/lightning_kokkos/lightning_kokkos.py index de531ba2fe..ae9ff2e2f4 100644 --- a/pennylane_lightning/lightning_kokkos/lightning_kokkos.py +++ b/pennylane_lightning/lightning_kokkos/lightning_kokkos.py @@ -63,6 +63,8 @@ from pennylane.operation import Tensor from pennylane.ops.op_math import Adjoint from pennylane.wires import Wires + + # pylint: disable=import-error, no-name-in-module, ungrouped-imports from pennylane_lightning.lightning_kokkos_ops.algorithms import ( AdjointJacobianC64, AdjointJacobianC128, From c3d33950b99ae88daffd9659ae89ff4667c564b7 Mon Sep 17 00:00:00 2001 From: albi3ro Date: Fri, 16 Feb 2024 13:46:56 -0500 Subject: [PATCH 170/428] update --- .../lightning_qubit/lightning_qubit2.py | 369 ++++++++++++++++++ 1 file changed, 369 insertions(+) create mode 100644 pennylane_lightning/lightning_qubit/lightning_qubit2.py diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py new file mode 100644 index 0000000000..cf32b7f178 --- /dev/null +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -0,0 +1,369 @@ +# Copyright 2018-2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This module contains the LightningQubit2 class that inherits from the new device interface. + +""" +from typing import Union, Sequence, Optional +from dataclasses import replace +import numpy as np + + +try: + # pylint: disable=import-error, no-name-in-module + from pennylane_lightning.lightning_qubit_ops import ( + StateVectorC64, + StateVectorC128, + ) + + LQ_CPP_BINARY_AVAILABLE = True + except ImportError: + LQ_CPP_BINARY_AVAILABLE = False + + +import pennylane as qml +from pennylane.devices import Device, ExecutionConfig, DefaultExecutionConfig +from pennylane.devices.modifiers import convert_single_circuit_to_batch, simulator_tracking +from pennylane.devices.preprocess import ( + decompose, + validate_device_wires, + decompose, + validate_measurements, + validate_observables, + no_sampling, +) +from pennylane.devices.qubit.sampling import get_num_shots_and_executions +from pennylane.tape import QuantumScript +from pennylane.transforms.core import TransformProgram +from pennylane.typing import Result, ResultBatch +from pennylane_lightning.lightning_qubit.device_modifiers import convert_single_circuit_to_batch + +from .device_modifiers import convert_single_circuit_to_batch + +def simulate( + circuit, + rng=None, + c_dtype=np.complex128, + batch_obs=False, + mcmc=False, + kernel_name="Local", + num_burnin=100, +): + """Calculate the results for a given circuit.""" + return 0.0 + + +def jacobian(circuit): + """Calculate the jacobian for a given circuit.""" + return np.array(0.0) + + +def simulate_and_jacobian(circuit): + """Calculate the results and jacobian for a single circuit.""" + return np.array(0.0), np.array(0.0) + + +Result_or_ResultBatch = Union[Result, ResultBatch] +QuantumTapeBatch = Sequence[qml.tape.QuantumTape] +QuantumTape_or_Batch = Union[qml.tape.QuantumTape, QuantumTapeBatch] + + +_operations = frozenset( + { + "Identity", + "BasisState", + "QubitStateVector", + "StatePrep", + "QubitUnitary", + "ControlledQubitUnitary", + "MultiControlledX", + "DiagonalQubitUnitary", + "PauliX", + "PauliY", + "PauliZ", + "MultiRZ", + "Hadamard", + "S", + "Adjoint(S)", + "T", + "Adjoint(T)", + "SX", + "Adjoint(SX)", + "CNOT", + "SWAP", + "ISWAP", + "PSWAP", + "Adjoint(ISWAP)", + "SISWAP", + "Adjoint(SISWAP)", + "SQISW", + "CSWAP", + "Toffoli", + "CY", + "CZ", + "PhaseShift", + "ControlledPhaseShift", + "CPhase", + "RX", + "RY", + "RZ", + "Rot", + "CRX", + "CRY", + "CRZ", + "C(PauliX)", + "C(PauliY)", + "C(PauliZ)", + "C(Hadamard)", + "C(S)", + "C(T)", + "C(PhaseShift)", + "C(RX)", + "C(RY)", + "C(RZ)", + "C(SWAP)", + "C(IsingXX)", + "C(IsingXY)", + "C(IsingYY)", + "C(IsingZZ)", + "C(SingleExcitation)", + "C(SingleExcitationMinus)", + "C(SingleExcitationPlus)", + "C(DoubleExcitation)", + "C(DoubleExcitationMinus)", + "C(DoubleExcitationPlus)", + "CRot", + "IsingXX", + "IsingYY", + "IsingZZ", + "IsingXY", + "SingleExcitation", + "SingleExcitationPlus", + "SingleExcitationMinus", + "DoubleExcitation", + "DoubleExcitationPlus", + "DoubleExcitationMinus", + "QubitCarry", + "QubitSum", + "OrbitalRotation", + "QFT", + "ECR", + "BlockEncode", + } +) +"""The set of supported operations.""" + +_observables = frozenset( + { + "PauliX", + "PauliY", + "PauliZ", + "Hadamard", + "Hermitian", + "Identity", + "Projector", + "SparseHamiltonian", + "Hamiltonian", + "Sum", + "SProd", + "Prod", + "Exp", + } +) +"""Test set of supported observables.""" + + +def stopping_condition(op: qml.operation.Operator) -> bool: + """A function that determines whether or not an operation is supported by ``lightning.qubit``.""" + return op.name in _operations + + +def accepted_observables(obs: qml.operation.Operator) -> bool: + """A function that determines whether or not an observable is supported by ``lightning.qubit``.""" + return obs.name in _observables + + +def accepted_analytic_measurements(m: qml.measurements.MeasurementProcess) -> bool: + """Whether or not a state based measurement is supported by ``lightning.qubit``.""" + return isinstance(m, (qml.measurements.ExpectationMP)) + + +@simulator_tracking +@convert_single_circuit_to_batch +class LightningQubit2(Device): + """PennyLane Lightning Qubit device. + + A device that interfaces with C++ to perform fast linear algebra calculations. + + Use of this device requires pre-built binaries or compilation from source. Check out the + :doc:`/lightning_qubit/installation` guide for more details. + + Args: + wires (int): the number of wires to initialize the device with + c_dtype: Datatypes for statevector representation. Must be one of + ``np.complex64`` or ``np.complex128``. + shots (int): How many times the circuit should be evaluated (or sampled) to estimate + the expectation values. Defaults to ``None`` if not specified. Setting + to ``None`` results in computing statistics like expectation values and + variances analytically. + seed (str, int, rng) + mcmc (bool): Determine whether to use the approximate Markov Chain Monte Carlo + sampling method when generating samples. + kernel_name (str): name of transition kernel. The current version supports + two kernels: ``"Local"`` and ``"NonZeroRandom"``. + The local kernel conducts a bit-flip local transition between states. + The local kernel generates a random qubit site and then generates a random + number to determine the new bit at that qubit site. The ``"NonZeroRandom"`` kernel + randomly transits between states that have nonzero probability. + num_burnin (int): number of steps that will be dropped. Increasing this value will + result in a closer approximation but increased runtime. + batch_obs (bool): Determine whether we process observables in parallel when + computing the jacobian. This value is only relevant when the lightning + qubit is built with OpenMP. + """ + + name = "lightning.qubit2" + + _device_options = ("rng", "c_dtype", "batch_obs", "mcmc", "kernel_name", "num_burnin") + + def __init__( # pylint: disable=too-many-arguments + self, + wires, + *, + c_dtype=np.complex128, + shots=None, + seed="global", + mcmc=False, + kernel_name="Local", + num_burnin=100, + batch_obs=False, + ): + if not LQ_CPP_BINARY_AVAILABLE: + raise ImportError("Pre-compiled binaries for lightning.qubit are not available. " + "To manually compile from source, follow the instructions at " + "https://pennylane-lightning.readthedocs.io/en/latest/installation.html.") + super().__init__(wires=wires, shots=shots) + seed = np.random.randint(0, high=10000000) if seed == "global" else seed + self._rng = np.random.default_rng(seed) + + self._c_dtype = c_dtype + self._batch_obs = batch_obs + self._mcmc = mcmc + if self._mcmc: + if kernel_name not in [ + "Local", + "NonZeroRandom", + ]: + raise NotImplementedError( + f"The {kernel_name} is not supported and currently " + "only 'Local' and 'NonZeroRandom' kernels are supported." + ) + if num_burnin >= shots: + raise ValueError("Shots should be greater than num_burnin.") + self._kernel_name = kernel_name + self._num_burnin = num_burnin + else: + self._kernel_name = None + self._num_burnin = None + + @property + def c_dtype(self): + """State vector complex data type.""" + return self._c_dtype + + @property + def operation(self) -> frozenset[str]: + """The names of the supported operations.""" + return _operations + + @property + def observables(self) -> frozenset[str]: + """The names of the supported observables.""" + return _observables + + def _setup_execution_config(self, config): + """ + Update the execution config with choices for how the device should be used and the device options. + """ + updated_values = {} + if config.gradient_method == "best": + updated_values["gradient_method"] = "adjoint" + if config.use_device_gradient is None: + updated_values["use_device_gradient"] = config.gradient_method in ("best", "adjoint") + if config.grad_on_execution is None: + updated_values["grad_on_execution"] = True + + new_device_options = dict(config.device_options) + for option in self._device_options: + if option not in new_device_options: + new_device_options[option] = getattr(self, f"_{option}", None) + + return replace(config, **updated_values, device_options=new_device_options) + + def supports_derivatives( + self, + execution_config: Optional[ExecutionConfig] = None, + circuit: Optional[qml.tape.QuantumTape] = None, + ) -> bool: + if execution_config is None and circuit is None: + return True + if execution_config.gradient_method not in {"adjoint", "best"}: + return False + if circuit is None: + return True + return ( + all(isinstance(m, qml.measurements.ExpectationMP) for m in circuit.measurements) + and not circuit.shots + ) + + def preprocess(self, execution_config: ExecutionConfig = DefaultExecutionConfig): + program = TransformProgram() + program.add_transform( + validate_measurements, analytic_measurements=accepted_analytic_measurements, name=self.name + ) + program.add_transform(no_sampling) + program.add_transform(validate_observables, accepted_observables, name=self.name) + program.add_transform(validate_device_wires, self.wires, name=self.name) + program.add_transform(qml.defer_measurements, device=self) + program.add_transform(decompose, stopping_condition=stopping_condition, name=self.name) + program.add_transform(qml.transforms.broadcast_expand) + return program, self._setup_execution_config(execution_config) + + def execute( + self, + circuits: QuantumTape_or_Batch, + execution_config: ExecutionConfig = DefaultExecutionConfig, + ) -> Result_or_ResultBatch: + + results = [] + for circuit in circuits: + circuit = circuit.map_to_standard_wires() + results.append(simulate(circuit, **execution_config.device_options)) + + return tuple(results) + + def compute_derivatives( + self, + circuits: QuantumTape_or_Batch, + execution_config: ExecutionConfig = DefaultExecutionConfig, + ): + return tuple(jacobian(circuit) for circuit in circuits) + + def execute_and_compute_derivatives( + self, + circuits: QuantumTape_or_Batch, + execution_config: ExecutionConfig = DefaultExecutionConfig, + ): + results = tuple(simulate_and_jacobian(c) for c in circuits) + return tuple(zip(*results)) From aae5771e1ca0ed9894066f0de6920e355051ed99 Mon Sep 17 00:00:00 2001 From: albi3ro Date: Fri, 16 Feb 2024 13:59:21 -0500 Subject: [PATCH 171/428] adding tests from add-simulate branch --- tests/conftest.py | 15 +- tests/lightning_qubit2/test_expval_2.py | 412 +++++++++++++++ tests/lightning_qubit2/test_serialize_2.py | 563 +++++++++++++++++++++ 3 files changed, 985 insertions(+), 5 deletions(-) create mode 100644 tests/lightning_qubit2/test_expval_2.py create mode 100644 tests/lightning_qubit2/test_serialize_2.py diff --git a/tests/conftest.py b/tests/conftest.py index 96531bf421..dd1fecc795 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -87,7 +87,7 @@ def n_subsystems(request): # Looking for the device for testing. default_device = "lightning.qubit" -supported_devices = {"lightning.kokkos", "lightning.qubit", "lightning.gpu"} +supported_devices = {"lightning.kokkos", "lightning.qubit", "lightning.qubit2", "lightning.gpu"} supported_devices.update({sb.replace(".", "_") for sb in supported_devices}) @@ -113,10 +113,10 @@ def get_device(): device_name = get_device() -if device_name not in qml.plugin_devices: - raise qml.DeviceError( - f"Device {device_name} does not exist. Make sure the required plugin is installed." - ) +# if device_name not in qml.plugin_devices: +# raise qml.DeviceError( +# f"Device {device_name} does not exist. Make sure the required plugin is installed." +# ) # Device specification import pennylane_lightning.lightning_qubit as lightning_ops # Any definition of lightning_ops will do @@ -131,6 +131,11 @@ def get_device(): if hasattr(pennylane_lightning, "lightning_gpu_ops"): import pennylane_lightning.lightning_gpu_ops as lightning_ops +elif device_name == "lightning.qubit2": + from pennylane_lightning.lightning_qubit import LightningQubit2 as LightningDevice + + if hasattr(pennylane_lightning, "lightning_qubit_ops"): + import pennylane_lightning.lightning_qubit_ops as lightning_ops else: from pennylane_lightning.lightning_qubit import LightningQubit as LightningDevice diff --git a/tests/lightning_qubit2/test_expval_2.py b/tests/lightning_qubit2/test_expval_2.py new file mode 100644 index 0000000000..78553c3e44 --- /dev/null +++ b/tests/lightning_qubit2/test_expval_2.py @@ -0,0 +1,412 @@ +# Copyright 2018-2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +Tests for process and execute (expval calculation). +""" +import pytest + +import numpy as np +import pennylane as qml +from pennylane_lightning.lightning_qubit import LightningQubit2 as LightningQubit +from pennylane.devices import DefaultQubit + +from conftest import LightningDevice # tested device + +if not LightningQubit._CPP_BINARY_AVAILABLE: + pytest.skip("No binary module found. Skipping.", allow_module_level=True) + +if LightningDevice != LightningQubit: + pytest.skip("Exclusive tests for lightning.qubit. Skipping.", allow_module_level=True) + +THETA = np.linspace(0.11, 1, 3) +PHI = np.linspace(0.32, 1, 3) +VARPHI = np.linspace(0.02, 1, 3) + + +@pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI))) +class TestExpval: + """Test expectation value calculations""" + + @pytest.fixture(params=[np.complex64, np.complex128]) + def dev(self, request): + return LightningQubit(c_dtype=request.param) + + @staticmethod + def calculate_reference(tape): + dev = DefaultQubit(max_workers=1) + program, _ = dev.preprocess() + tapes, transf_fn = program([tape]) + results = dev.execute(tapes) + return transf_fn(results) + + @staticmethod + def process_and_execute(dev, tape): + program, _ = dev.preprocess() + tapes, transf_fn = program([tape]) + results = dev.execute(tapes) + return transf_fn(results) + + def test_Identity(self, theta, phi, dev, tol): + """Tests applying identities.""" + + with qml.tape.QuantumTape() as tape: + qml.Identity(wires=[0]) + qml.Identity(wires=[0, 1]) + qml.Identity(wires=[1, 2]) + qml.RX(theta, wires=[0]) + qml.RX(phi, wires=[1]) + qml.expval(qml.PauliX(0)) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_identity_expectation(self, theta, phi, dev, tol): + """Tests identity.""" + + tape = qml.tape.QuantumScript( + [qml.RX(theta, wires=[0]), qml.RX(phi, wires=[1]), qml.CNOT(wires=[0, 1])], + [qml.expval(qml.Identity(wires=[0])), qml.expval(qml.Identity(wires=[1]))], + ) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_multi_wire_identity_expectation(self, theta, phi, dev, tol): + """Tests multi-wire identity.""" + + tape = qml.tape.QuantumScript( + [qml.RX(theta, wires=[0]), qml.RX(phi, wires=[1]), qml.CNOT(wires=[0, 1])], + [qml.expval(qml.Identity(wires=[0, 1]))], + ) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + @pytest.mark.parametrize( + "wires", + [ + ([0, 1]), + (["a", 1]), + (["b", "a"]), + ], + ) + def test_PauliZ_expectation(self, theta, phi, dev, tol, wires): + """Tests PauliZ.""" + + tape = qml.tape.QuantumScript( + [qml.RX(theta, wires=wires[0]), qml.RX(phi, wires=wires[1]), qml.CNOT(wires=wires)], + [qml.expval(qml.PauliZ(wires=wires[0])), qml.expval(qml.PauliZ(wires=wires[1]))], + ) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_PauliX_expectation(self, theta, phi, dev, tol): + """Tests PauliX.""" + + tape = qml.tape.QuantumScript( + [qml.RY(theta, wires=[0]), qml.RY(phi, wires=[1]), qml.CNOT(wires=[0, 1])], + [qml.expval(qml.PauliX(wires=[0])), qml.expval(qml.PauliX(wires=[1]))], + ) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_PauliY_expectation(self, theta, phi, dev, tol): + """Tests PauliY.""" + + tape = qml.tape.QuantumScript( + [qml.RX(theta, wires=[0]), qml.RX(phi, wires=[1]), qml.CNOT(wires=[0, 1])], + [qml.expval(qml.PauliY(wires=[0])), qml.expval(qml.PauliY(wires=[1]))], + ) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_hadamard_expectation(self, theta, phi, dev, tol): + """Tests Hadamard.""" + + tape = qml.tape.QuantumScript( + [qml.RY(theta, wires=[0]), qml.RY(phi, wires=[1]), qml.CNOT(wires=[0, 1])], + [qml.expval(qml.Hadamard(wires=[0])), qml.expval(qml.Hadamard(wires=[1]))], + ) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_hermitian_expectation(self, theta, phi, dev, tol): + """Tests an Hermitian operator.""" + + with qml.tape.QuantumTape() as tape: + qml.RX(theta, wires=0) + qml.RX(phi, wires=1) + qml.RX(theta + phi, wires=2) + + for idx in range(3): + qml.expval(qml.Hermitian([[1, 0], [0, -1]], wires=[idx])) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_hamiltonian_expectation(self, theta, phi, dev, tol): + """Tests a Hamiltonian.""" + + ham = qml.Hamiltonian( + [1.0, 0.3, 0.3, 0.4], + [ + qml.PauliX(0) @ qml.PauliX(1), + qml.PauliZ(0), + qml.PauliZ(1), + qml.PauliX(0) @ qml.PauliY(1), + ], + ) + + with qml.tape.QuantumTape() as tape: + qml.RX(theta, wires=0) + qml.RX(phi, wires=1) + qml.RX(theta + phi, wires=2) + + qml.expval(ham) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_sparse_hamiltonian_expectation(self, theta, phi, dev, tol): + """Tests a Hamiltonian.""" + + ham = qml.SparseHamiltonian( + qml.Hamiltonian( + [1.0, 0.3, 0.3, 0.4], + [ + qml.PauliX(0) @ qml.PauliX(1), + qml.PauliZ(0), + qml.PauliZ(1), + qml.PauliX(0) @ qml.PauliY(1), + ], + ).sparse_matrix(), + wires=[0, 1], + ) + + with qml.tape.QuantumTape() as tape: + qml.RX(theta, wires=0) + qml.RX(phi, wires=1) + + qml.expval(ham) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + +@pytest.mark.parametrize("phi", PHI) +class TestOperatorArithmetic: + """Test integration with SProd, Prod, and Sum.""" + + @pytest.fixture(params=[np.complex64, np.complex128]) + def dev(self, request): + return LightningQubit(c_dtype=request.param) + + @staticmethod + def calculate_reference(tape): + dev = DefaultQubit(max_workers=1) + program, _ = dev.preprocess() + tapes, transf_fn = program([tape]) + results = dev.execute(tapes) + return transf_fn(results) + + @staticmethod + def process_and_execute(dev, tape): + program, _ = dev.preprocess() + tapes, transf_fn = program([tape]) + results = dev.execute(tapes) + return transf_fn(results) + + def test_s_prod(self, phi, dev, tol): + """Tests the `SProd` class.""" + + tape = qml.tape.QuantumScript( + [qml.RX(phi, wires=[0])], + [qml.expval(qml.s_prod(0.5, qml.PauliZ(0)))], + ) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_prod(self, phi, dev, tol): + """Tests the `Prod` class.""" + + tape = qml.tape.QuantumScript( + [qml.RX(phi, wires=[0]), qml.Hadamard(wires=[1]), qml.PauliZ(wires=[1])], + [qml.expval(qml.prod(qml.PauliZ(0), qml.PauliX(1)))], + ) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_sum(self, phi, dev, tol): + """Tests the `Sum` class.""" + tape = qml.tape.QuantumScript( + [qml.RX(phi, wires=[0]), qml.RX(-1.1 * phi, wires=[0])], + [qml.expval(qml.sum(qml.PauliZ(0), qml.PauliX(1)))], + ) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_integration(self, phi, dev, tol): + """Test a Combination of `Sum`, `SProd`, and `Prod`.""" + + obs = qml.sum(qml.s_prod(2.3, qml.PauliZ(0)), -0.5 * qml.prod(qml.PauliY(0), qml.PauliZ(1))) + + tape = qml.tape.QuantumScript( + [qml.RX(phi, wires=[0]), qml.RX(-1.1 * phi, wires=[0])], + [qml.expval(obs)], + ) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + +@pytest.mark.parametrize("theta, phi, varphi", list(zip(THETA, PHI, VARPHI))) +class TestTensorExpval: + """Test tensor expectation values""" + + @pytest.fixture(params=[np.complex64, np.complex128]) + def dev(self, request): + return LightningQubit(c_dtype=request.param) + + @staticmethod + def calculate_reference(tape): + dev = DefaultQubit(max_workers=1) + program, _ = dev.preprocess() + tapes, transf_fn = program([tape]) + results = dev.execute(tapes) + return transf_fn(results) + + @staticmethod + def process_and_execute(dev, tape): + program, _ = dev.preprocess() + tapes, transf_fn = program([tape]) + results = dev.execute(tapes) + return transf_fn(results) + + def test_PauliX_PauliY(self, theta, phi, varphi, dev, tol): + """Tests a tensor product involving PauliX and PauliY.""" + + with qml.tape.QuantumTape() as tape: + qml.RX(theta, wires=[0]) + qml.RX(phi, wires=[1]) + qml.RX(varphi, wires=[2]) + qml.CNOT(wires=[0, 1]) + qml.CNOT(wires=[1, 2]) + qml.expval(qml.PauliX(0) @ qml.PauliY(2)) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_PauliZ_identity(self, theta, phi, varphi, dev, tol): + """Tests a tensor product involving PauliZ and Identity.""" + + with qml.tape.QuantumTape() as tape: + qml.Identity(wires=[0]) + qml.RX(theta, wires=[0]) + qml.RX(phi, wires=[1]) + qml.RX(varphi, wires=[2]) + qml.CNOT(wires=[0, 1]) + qml.CNOT(wires=[1, 2]) + qml.expval(qml.PauliZ(0) @ qml.Identity(1) @ qml.PauliZ(2)) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) + + def test_PauliZ_hadamard_PauliY(self, theta, phi, varphi, dev, tol): + """Tests a tensor product involving PauliY, PauliZ and Hadamard.""" + + with qml.tape.QuantumTape() as tape: + qml.RX(theta, wires=[0]) + qml.RX(phi, wires=[1]) + qml.RX(varphi, wires=[2]) + qml.CNOT(wires=[0, 1]) + qml.CNOT(wires=[1, 2]) + qml.expval(qml.PauliZ(0) @ qml.Hadamard(1) @ qml.PauliY(2)) + + calculated_val = self.process_and_execute(dev, tape) + reference_val = self.calculate_reference(tape) + + tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 + + assert np.allclose(calculated_val, reference_val, tol) diff --git a/tests/lightning_qubit2/test_serialize_2.py b/tests/lightning_qubit2/test_serialize_2.py new file mode 100644 index 0000000000..7f14422c38 --- /dev/null +++ b/tests/lightning_qubit2/test_serialize_2.py @@ -0,0 +1,563 @@ +# Copyright 2018-2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +Unit tests for the serialization helper functions. +""" +import pytest +from conftest import device_name, LightningDevice + +import numpy as np +import pennylane as qml +from pennylane_lightning.lightning_qubit._serialize import QuantumScriptSerializer + +# from pennylane_lightning.lightning_qubit._serialize_old import QuantumScriptSerializer + +if not LightningDevice._CPP_BINARY_AVAILABLE: + pytest.skip("No binary module found. Skipping.", allow_module_level=True) + +if device_name == "lightning.kokkos": + from pennylane_lightning.lightning_kokkos_ops.observables import ( + NamedObsC64, + NamedObsC128, + HermitianObsC64, + HermitianObsC128, + TensorProdObsC64, + TensorProdObsC128, + HamiltonianC64, + HamiltonianC128, + SparseHamiltonianC64, + SparseHamiltonianC128, + ) +elif device_name == "lightning.gpu": + from pennylane_lightning.lightning_gpu_ops.observables import ( + NamedObsC64, + NamedObsC128, + HermitianObsC64, + HermitianObsC128, + TensorProdObsC64, + TensorProdObsC128, + HamiltonianC64, + HamiltonianC128, + SparseHamiltonianC64, + SparseHamiltonianC128, + ) +else: + from pennylane_lightning.lightning_qubit_ops.observables import ( + NamedObsC64, + NamedObsC128, + HermitianObsC64, + HermitianObsC128, + TensorProdObsC64, + TensorProdObsC128, + HamiltonianC64, + HamiltonianC128, + SparseHamiltonianC64, + SparseHamiltonianC128, + ) + + +def test_wrong_device_name(): + """Test the device name is not a valid option""" + + with pytest.raises(qml.DeviceError, match="The device name"): + QuantumScriptSerializer("thunder.qubit") + + +@pytest.mark.parametrize( + "obs,obs_type", + [ + (qml.PauliZ(0), NamedObsC128), + (qml.PauliZ(0) @ qml.PauliZ(1), TensorProdObsC128), + (qml.Hadamard(0), NamedObsC128), + (qml.Hermitian(np.eye(2), wires=0), HermitianObsC128), + ( + qml.PauliZ(0) @ qml.Hadamard(1) @ (0.1 * (qml.PauliZ(2) + qml.PauliX(3))), + HamiltonianC128, + ), + ( + ( + qml.Hermitian(np.eye(2), wires=0) + @ qml.Hermitian(np.eye(2), wires=1) + @ qml.Projector([0], wires=2) + ), + TensorProdObsC128, + ), + ( + qml.PauliZ(0) @ qml.Hermitian(np.eye(2), wires=1) @ qml.Projector([0], wires=2), + TensorProdObsC128, + ), + (qml.Projector([0], wires=0), HermitianObsC128), + (qml.Hamiltonian([1], [qml.PauliZ(0)]), HamiltonianC128), + (qml.sum(qml.Hadamard(0), qml.PauliX(1)), HermitianObsC128), + ( + qml.SparseHamiltonian(qml.Hamiltonian([1], [qml.PauliZ(0)]).sparse_matrix(), wires=[0]), + SparseHamiltonianC128, + ), + ], +) +def test_obs_returns_expected_type(obs, obs_type): + """Tests that observables get serialized to the expected type.""" + assert isinstance(QuantumScriptSerializer(device_name)._ob(obs), obs_type) + + +class TestSerializeObs: + """Tests for the serialize_observables function""" + + @pytest.mark.parametrize("use_csingle", [True, False]) + def test_tensor_non_tensor_return(self, use_csingle): + """Test expected serialization for a mixture of tensor product and non-tensor product + return""" + with qml.tape.QuantumTape() as tape: + qml.expval(qml.PauliZ(0) @ qml.PauliX(1)) + qml.expval(qml.Hadamard(1)) + + tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 + named_obs = NamedObsC64 if use_csingle else NamedObsC128 + + s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) + + s_expected = [ + tensor_prod_obs([named_obs("PauliZ", [0]), named_obs("PauliX", [1])]), + named_obs("Hadamard", [1]), + ] + + assert s == s_expected + + @pytest.mark.parametrize("use_csingle", [True, False]) + def test_hermitian_return(self, use_csingle): + """Test expected serialization for a Hermitian return""" + with qml.tape.QuantumTape() as tape: + qml.expval(qml.Hermitian(np.eye(4), wires=[0, 1])) + + hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 + c_dtype = np.complex64 if use_csingle else np.complex128 + + s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) + s_expected = hermitian_obs( + np.array( + [1.0, 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, 1.0], + dtype=c_dtype, + ), + [0, 1], + ) + assert s[0] == s_expected + + @pytest.mark.parametrize("use_csingle", [True, False]) + def test_hermitian_tensor_return(self, use_csingle): + """Test expected serialization for a Hermitian return""" + with qml.tape.QuantumTape() as tape: + qml.expval(qml.Hermitian(np.eye(4), wires=[0, 1]) @ qml.Hermitian(np.eye(2), wires=[2])) + + c_dtype = np.complex64 if use_csingle else np.complex128 + tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 + hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 + s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) + + s_expected = tensor_prod_obs( + [ + hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), + hermitian_obs(np.eye(2, dtype=c_dtype).ravel(), [2]), + ] + ) + + assert s[0] == s_expected + + @pytest.mark.parametrize("use_csingle", [True, False]) + def test_mixed_tensor_return(self, use_csingle): + """Test expected serialization for a mixture of Hermitian and Pauli return""" + with qml.tape.QuantumTape() as tape: + qml.expval(qml.Hermitian(np.eye(4), wires=[0, 1]) @ qml.PauliY(2)) + + c_dtype = np.complex64 if use_csingle else np.complex128 + tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 + hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 + named_obs = NamedObsC64 if use_csingle else NamedObsC128 + + s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) + + s_expected = tensor_prod_obs( + [hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), named_obs("PauliY", [2])] + ) + + assert s[0] == s_expected + + @pytest.mark.parametrize("use_csingle", [True, False]) + def test_hamiltonian_return(self, use_csingle): + """Test expected serialization for a Hamiltonian return""" + + ham = qml.Hamiltonian( + [0.3, 0.5, 0.4], + [ + qml.Hermitian(np.eye(4), wires=[0, 1]) @ qml.PauliY(2), + qml.PauliX(0) @ qml.PauliY(2), + qml.Hermitian(np.ones((8, 8)), wires=range(3)), + ], + ) + + with qml.tape.QuantumTape() as tape: + qml.expval(ham) + + hamiltonian_obs = HamiltonianC64 if use_csingle else HamiltonianC128 + named_obs = NamedObsC64 if use_csingle else NamedObsC128 + hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 + tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 + r_dtype = np.float32 if use_csingle else np.float64 + c_dtype = np.complex64 if use_csingle else np.complex128 + + s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) + + s_expected = hamiltonian_obs( + np.array([0.3, 0.5, 0.4], dtype=r_dtype), + [ + tensor_prod_obs( + [ + hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), + named_obs("PauliY", [2]), + ] + ), + tensor_prod_obs([named_obs("PauliX", [0]), named_obs("PauliY", [2])]), + hermitian_obs(np.ones(64, dtype=c_dtype), [0, 1, 2]), + ], + ) + + assert s[0] == s_expected + + @pytest.mark.parametrize("use_csingle", [True, False]) + def test_hamiltonian_tensor_return(self, use_csingle): + """Test expected serialization for a Hamiltonian return""" + + with qml.tape.QuantumTape() as tape: + ham = qml.Hamiltonian( + [0.3, 0.5, 0.4], + [ + qml.Hermitian(np.eye(4), wires=[0, 1]) @ qml.PauliY(2), + qml.PauliX(0) @ qml.PauliY(2), + qml.Hermitian(np.ones((8, 8)), wires=range(3)), + ], + ) + qml.expval(ham @ qml.PauliZ(3)) + + hamiltonian_obs = HamiltonianC64 if use_csingle else HamiltonianC128 + named_obs = NamedObsC64 if use_csingle else NamedObsC128 + hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 + tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 + r_dtype = np.float32 if use_csingle else np.float64 + c_dtype = np.complex64 if use_csingle else np.complex128 + + s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) + + # Expression (ham @ obs) is converted internally by Pennylane + # where obs is appended to each term of the ham + s_expected = hamiltonian_obs( + np.array([0.3, 0.5, 0.4], dtype=r_dtype), + [ + tensor_prod_obs( + [ + hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), + named_obs("PauliY", [2]), + named_obs("PauliZ", [3]), + ] + ), + tensor_prod_obs( + [named_obs("PauliX", [0]), named_obs("PauliY", [2]), named_obs("PauliZ", [3])] + ), + tensor_prod_obs( + [hermitian_obs(np.ones(64, dtype=c_dtype), [0, 1, 2]), named_obs("PauliZ", [3])] + ), + ], + ) + + assert s[0] == s_expected + + @pytest.mark.parametrize("use_csingle", [True, False]) + def test_hamiltonian_mix_return(self, use_csingle): + """Test expected serialization for a Hamiltonian return""" + + ham1 = qml.Hamiltonian( + [0.3, 0.5, 0.4], + [ + qml.Hermitian(np.eye(4), wires=[0, 1]) @ qml.PauliY(2), + qml.PauliX(0) @ qml.PauliY(2), + qml.Hermitian(np.ones((8, 8)), wires=range(3)), + ], + ) + ham2 = qml.Hamiltonian( + [0.7, 0.3], + [qml.PauliX(0) @ qml.Hermitian(np.eye(4), wires=[1, 2]), qml.PauliY(0) @ qml.PauliX(2)], + ) + + with qml.tape.QuantumTape() as tape: + qml.expval(ham1) + qml.expval(ham2) + + hamiltonian_obs = HamiltonianC64 if use_csingle else HamiltonianC128 + named_obs = NamedObsC64 if use_csingle else NamedObsC128 + hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 + tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 + r_dtype = np.float32 if use_csingle else np.float64 + c_dtype = np.complex64 if use_csingle else np.complex128 + + s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) + + s_expected1 = hamiltonian_obs( + np.array([0.3, 0.5, 0.4], dtype=r_dtype), + [ + tensor_prod_obs( + [ + hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), + named_obs("PauliY", [2]), + ] + ), + tensor_prod_obs([named_obs("PauliX", [0]), named_obs("PauliY", [2])]), + hermitian_obs(np.ones(64, dtype=c_dtype), [0, 1, 2]), + ], + ) + s_expected2 = hamiltonian_obs( + np.array([0.7, 0.3], dtype=r_dtype), + [ + tensor_prod_obs( + [ + named_obs("PauliX", [0]), + hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [1, 2]), + ] + ), + tensor_prod_obs([named_obs("PauliY", [0]), named_obs("PauliX", [2])]), + ], + ) + + assert s[0] == s_expected1 + assert s[1] == s_expected2 + + @pytest.mark.parametrize( + "obs,coeffs,terms", + [ + (qml.prod(qml.PauliZ(0), qml.PauliX(1)), [1], [[("PauliX", 1), ("PauliZ", 0)]]), + (qml.s_prod(0.1, qml.PauliX(0)), [0.1], ("PauliX", 0)), + ( + qml.sum( + 0.5 * qml.prod(qml.PauliX(0), qml.PauliZ(1)), + 0.1 * qml.prod(qml.PauliZ(0), qml.PauliY(1)), + ), + [0.5, 0.1], + [[("PauliZ", 1), ("PauliX", 0)], [("PauliY", 1), ("PauliZ", 0)]], + ), + ], + ) + @pytest.mark.parametrize("use_csingle", [True, False]) + def test_op_arithmetic_uses_hamiltonian(self, use_csingle, obs, coeffs, terms): + """Tests that an arithmetic obs with a PauliRep serializes as a Hamiltonian.""" + tape = qml.tape.QuantumTape(measurements=[qml.expval(obs)]) + res, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) + assert len(res) == 1 + assert isinstance(res[0], HamiltonianC64 if use_csingle else HamiltonianC128) + + hamiltonian_obs = HamiltonianC64 if use_csingle else HamiltonianC128 + tensor_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 + named_obs = NamedObsC64 if use_csingle else NamedObsC128 + rtype = np.float32 if use_csingle else np.float64 + term_shape = np.array(terms).shape + + if len(term_shape) == 1: # just a single pauli op + expected_terms = [named_obs(terms[0], [terms[1]])] + elif len(term_shape) == 3: # list of tensor products + expected_terms = [ + tensor_obs([named_obs(pauli, [wire]) for pauli, wire in term]) for term in terms + ] + + coeffs = np.array(coeffs).astype(rtype) + assert res[0] == hamiltonian_obs(coeffs, expected_terms) + + @pytest.mark.parametrize("use_csingle", [True, False]) + def test_multi_wire_identity(self, use_csingle): + """Tests that multi-wire Identity does not fail serialization.""" + tape = qml.tape.QuantumTape(measurements=[qml.expval(qml.Identity(wires=[1, 2]))]) + res, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) + assert len(res) == 1 + + named_obs = NamedObsC64 if use_csingle else NamedObsC128 + assert res[0] == named_obs("Identity", [1]) + + +class TestSerializeOps: + """Tests for the _ops function""" + + def test_basic_circuit(self): + """Test expected serialization for a simple circuit""" + with qml.tape.QuantumTape() as tape: + qml.RX(0.4, wires=0) + qml.RY(0.6, wires=1) + qml.CNOT(wires=[0, 1]) + + s = QuantumScriptSerializer(device_name).serialize_ops(tape) + s_expected = ( + ( + ["RX", "RY", "CNOT"], + [np.array([0.4]), np.array([0.6]), []], + [[0], [1], [0, 1]], + [False, False, False], + [[], [], []], + [[], [], []], + [[], [], []], + ), + False, + ) + assert s == s_expected + + def test_basic_circuit_not_implemented_ctrl_ops(self): + """Test expected serialization for a simple circuit""" + ops = qml.OrbitalRotation(0.1234, wires=range(4)) + with qml.tape.QuantumTape() as tape: + qml.RX(0.4, wires=0) + qml.RY(0.6, wires=1) + qml.ctrl(ops, [4, 5]) + + s = QuantumScriptSerializer(device_name).serialize_ops(tape) + s_expected = ( + ( + ["RX", "RY", "QubitUnitary"], + [np.array([0.4]), np.array([0.6]), [0.0]], + [[0], [1], list(ops.wires)], + [False, False, False], + [[], [], [qml.matrix(ops)]], + [[], [], [4, 5]], + ), + False, + ) + assert s[0][0] == s_expected[0][0] + assert s[0][1] == s_expected[0][1] + assert s[0][2] == s_expected[0][2] + assert s[0][3] == s_expected[0][3] + assert all(np.allclose(s0, s1) for s0, s1 in zip(s[0][4], s_expected[0][4])) + assert s[0][5] == s_expected[0][5] + assert s[1] == s_expected[1] + + def test_multicontrolledx(self): + """Test expected serialization for a simple circuit""" + with qml.tape.QuantumTape() as tape: + qml.RX(0.4, wires=0) + qml.RY(0.6, wires=1) + qml.ctrl(qml.PauliX(wires=0), [1, 2, 3], control_values=[True, False, False]) + + s = QuantumScriptSerializer(device_name).serialize_ops(tape) + s_expected = ( + ( + ["RX", "RY", "PauliX"], + [np.array([0.4]), np.array([0.6]), []], + [[0], [1], [0]], + [False, False, False], + [[], [], []], + [[], [], [1, 2, 3]], + [[], [], [True, False, False]], + ), + False, + ) + assert s == s_expected + + @pytest.mark.parametrize("stateprep", [qml.QubitStateVector, qml.StatePrep]) + def test_skips_prep_circuit(self, stateprep): + """Test expected serialization for a simple circuit with state preparation, such that + the state preparation is skipped""" + with qml.tape.QuantumTape() as tape: + stateprep([1, 0], wires=0) + qml.BasisState([1], wires=1) + qml.RX(0.4, wires=0) + qml.RY(0.6, wires=1) + qml.CNOT(wires=[0, 1]) + + s = QuantumScriptSerializer(device_name).serialize_ops(tape) + s_expected = ( + ( + ["RX", "RY", "CNOT"], + [[0.4], [0.6], []], + [[0], [1], [0, 1]], + [False, False, False], + [[], [], []], + [[], [], []], + [[], [], []], + ), + True, + ) + assert s == s_expected + + def test_unsupported_kernel_circuit(self): + """Test expected serialization for a circuit including gates that do not have a dedicated + kernel""" + with qml.tape.QuantumTape() as tape: + qml.CNOT(wires=[0, 1]) + qml.RZ(0.2, wires=2) + + s = QuantumScriptSerializer(device_name).serialize_ops(tape) + s_expected = ( + ( + ["CNOT", "RZ"], + [[], [0.2]], + [[0, 1], [2]], + [False, False], + ), + False, + ) + assert s[0][0] == s_expected[0][0] + assert s[0][1] == s_expected[0][1] + + @pytest.mark.parametrize("C", [True, False]) + def test_integration(self, C): + """Test expected serialization for a random circuit""" + with qml.tape.QuantumTape() as tape: + qml.RX(0.4, wires=0) + qml.RY(0.6, wires=1) + qml.CNOT(wires=[0, 1]) + qml.QubitUnitary(np.eye(4), wires=[0, 1]) + qml.templates.QFT(wires=[0, 1, 2]) + qml.DoubleExcitation(0.555, wires=[3, 2, 1, 0]) + qml.DoubleExcitationMinus(0.555, wires=[0, 1, 2, 3]) + qml.DoubleExcitationPlus(0.555, wires=[0, 1, 2, 3]) + + s = QuantumScriptSerializer(device_name).serialize_ops(tape) + + dtype = np.complex64 if C else np.complex128 + s_expected = ( + ( + [ + "RX", + "RY", + "CNOT", + "QubitUnitary", + "QFT", + "DoubleExcitation", + "DoubleExcitationMinus", + "DoubleExcitationPlus", + ], + [[0.4], [0.6], [], [0.0], [], [0.555], [0.555], [0.555]], + [[0], [1], [0, 1], [0, 1], [0, 1, 2], [3, 2, 1, 0], [0, 1, 2, 3], [0, 1, 2, 3]], + [False, False, False, False, False, False, False, False], + [ + [], + [], + [], + qml.matrix(qml.QubitUnitary(np.eye(4, dtype=dtype), wires=[0, 1])), + qml.matrix(qml.templates.QFT(wires=[0, 1, 2])), + [], + [], + [], + ], + ), + False, + ) + assert s[0][0] == s_expected[0][0] + assert s[0][1] == s_expected[0][1] + assert s[0][2] == s_expected[0][2] + assert s[0][3] == s_expected[0][3] + assert s[1] == s_expected[1] + + assert all(np.allclose(s1, s2) for s1, s2 in zip(s[0][4], s_expected[0][4])) From a92d73aebacaf168d681c0a7f13f6e4b2e226c23 Mon Sep 17 00:00:00 2001 From: albi3ro Date: Fri, 16 Feb 2024 15:08:00 -0500 Subject: [PATCH 172/428] merge conflicts --- .../lightning_qubit/lightning_qubit2.py | 63 ++++++++++++++----- 1 file changed, 47 insertions(+), 16 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index cf32b7f178..507c7600d5 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -15,23 +15,10 @@ This module contains the LightningQubit2 class that inherits from the new device interface. """ -from typing import Union, Sequence, Optional +from typing import Optional, Union, Sequence, Callable from dataclasses import replace import numpy as np - -try: - # pylint: disable=import-error, no-name-in-module - from pennylane_lightning.lightning_qubit_ops import ( - StateVectorC64, - StateVectorC128, - ) - - LQ_CPP_BINARY_AVAILABLE = True - except ImportError: - LQ_CPP_BINARY_AVAILABLE = False - - import pennylane as qml from pennylane.devices import Device, ExecutionConfig, DefaultExecutionConfig from pennylane.devices.modifiers import convert_single_circuit_to_batch, simulator_tracking @@ -43,8 +30,7 @@ validate_observables, no_sampling, ) -from pennylane.devices.qubit.sampling import get_num_shots_and_executions -from pennylane.tape import QuantumScript +from pennylane.tape import QuantumTape from pennylane.transforms.core import TransformProgram from pennylane.typing import Result, ResultBatch from pennylane_lightning.lightning_qubit.device_modifiers import convert_single_circuit_to_batch @@ -63,7 +49,14 @@ def simulate( """Calculate the results for a given circuit.""" return 0.0 +try: + # pylint: disable=import-error, no-name-in-module + from pennylane_lightning.lightning_qubit_ops import ( + StateVectorC64, + StateVectorC128, + ) +<<<<<<< HEAD def jacobian(circuit): """Calculate the jacobian for a given circuit.""" return np.array(0.0) @@ -79,6 +72,44 @@ def simulate_and_jacobian(circuit): QuantumTape_or_Batch = Union[qml.tape.QuantumTape, QuantumTapeBatch] +======= + LQ_CPP_BINARY_AVAILABLE = True +except ImportError: + LQ_CPP_BINARY_AVAILABLE = False + +Result_or_ResultBatch = Union[Result, ResultBatch] +QuantumTapeBatch = Sequence[QuantumTape] +QuantumTape_or_Batch = Union[QuantumTape, QuantumTapeBatch] +PostprocessingFn = Callable[[ResultBatch], Result_or_ResultBatch] + + +def simulate(circuit: QuantumTape, dtype=np.complex128) -> Result: + """Simulate a single quantum script.a + + Args: + circuit (QuantumTape): The single circuit to simulate + dtype: Datatypes for state-vector representation. Must be one of + ``np.complex64`` or ``np.complex128``. + + Returns: + tuple(TensorLike): The results of the simulation + + Note that this function can return measurements for non-commuting observables simultaneously. + + """ + state = LightningStateVector(num_wires=circuit.num_wires, dtype=dtype).get_final_state(circuit) + return LightningMeasurements(state).measure_final_state(circuit) + + +def dummy_jacobian(circuit: QuantumTape): + return np.array(0.0) + + +def simulate_and_jacobian(circuit: QuantumTape): + return np.array(0.0), np.array(0.0) + + +>>>>>>> 2a8cd8da (merge conflicts) _operations = frozenset( { "Identity", From d7b38a237246db1fec4b2d69ab9f870e2a581742 Mon Sep 17 00:00:00 2001 From: albi3ro Date: Fri, 16 Feb 2024 15:51:29 -0500 Subject: [PATCH 173/428] create state vector on initialization --- .../lightning_qubit/lightning_qubit2.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index 507c7600d5..51d9b5abde 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -83,6 +83,7 @@ def simulate_and_jacobian(circuit): PostprocessingFn = Callable[[ResultBatch], Result_or_ResultBatch] +<<<<<<< HEAD def simulate(circuit: QuantumTape, dtype=np.complex128) -> Result: """Simulate a single quantum script.a @@ -90,6 +91,14 @@ def simulate(circuit: QuantumTape, dtype=np.complex128) -> Result: circuit (QuantumTape): The single circuit to simulate dtype: Datatypes for state-vector representation. Must be one of ``np.complex64`` or ``np.complex128``. +======= +def simulate(circuit: QuantumScript, state: LightningStateVector) -> Result: + """Simulate a single quantum script. + + Args: + circuit (QuantumTape): The single circuit to simulate + state (LightningStateVector): handle to Lightning state vector +>>>>>>> fde61720 (create state vector on initialization) Returns: tuple(TensorLike): The results of the simulation @@ -101,7 +110,7 @@ def simulate(circuit: QuantumTape, dtype=np.complex128) -> Result: return LightningMeasurements(state).measure_final_state(circuit) -def dummy_jacobian(circuit: QuantumTape): +def jacobian(circuit: QuantumTape): return np.array(0.0) @@ -285,6 +294,8 @@ def __init__( # pylint: disable=too-many-arguments "To manually compile from source, follow the instructions at " "https://pennylane-lightning.readthedocs.io/en/latest/installation.html.") super().__init__(wires=wires, shots=shots) + + self._statevector = LightningStateVector(num_wires=len(self.wires), dtype=c_dtype) seed = np.random.randint(0, high=10000000) if seed == "global" else seed self._rng = np.random.default_rng(seed) @@ -380,7 +391,7 @@ def execute( results = [] for circuit in circuits: circuit = circuit.map_to_standard_wires() - results.append(simulate(circuit, **execution_config.device_options)) + results.append(simulate(circuit, self._statevector)) return tuple(results) From 6a737554a5d5fb8ef9591f2efb03971bb46c63ed Mon Sep 17 00:00:00 2001 From: albi3ro Date: Fri, 16 Feb 2024 15:52:23 -0500 Subject: [PATCH 174/428] remove import of modifier from lightning --- pennylane_lightning/lightning_qubit/lightning_qubit2.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index 51d9b5abde..7b8772a914 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -33,7 +33,6 @@ from pennylane.tape import QuantumTape from pennylane.transforms.core import TransformProgram from pennylane.typing import Result, ResultBatch -from pennylane_lightning.lightning_qubit.device_modifiers import convert_single_circuit_to_batch from .device_modifiers import convert_single_circuit_to_batch From 0ec508972169e11d614928c6f1816b0ecda68dde Mon Sep 17 00:00:00 2001 From: Christina Lee Date: Fri, 16 Feb 2024 16:05:48 -0500 Subject: [PATCH 175/428] Update pennylane_lightning/lightning_qubit/lightning_qubit2.py --- pennylane_lightning/lightning_qubit/lightning_qubit2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index 7b8772a914..ff0e362828 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -324,7 +324,7 @@ def c_dtype(self): return self._c_dtype @property - def operation(self) -> frozenset[str]: + def operations(self) -> frozenset[str]: """The names of the supported operations.""" return _operations From 5646c8289942e5758ab319a06b296a798319495d Mon Sep 17 00:00:00 2001 From: albi3ro Date: Wed, 21 Feb 2024 11:20:49 -0500 Subject: [PATCH 176/428] minor test updates --- .../lightning_qubit/lightning_qubit2.py | 118 +++--------------- tests/lightning_qubit2/test_expval_2.py | 57 ++++----- 2 files changed, 46 insertions(+), 129 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index ff0e362828..2153a201ae 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -13,7 +13,6 @@ # limitations under the License. """ This module contains the LightningQubit2 class that inherits from the new device interface. - """ from typing import Optional, Union, Sequence, Callable from dataclasses import replace @@ -21,7 +20,7 @@ import pennylane as qml from pennylane.devices import Device, ExecutionConfig, DefaultExecutionConfig -from pennylane.devices.modifiers import convert_single_circuit_to_batch, simulator_tracking +from pennylane.devices.modifiers import single_tape_support, simulator_tracking from pennylane.devices.preprocess import ( decompose, validate_device_wires, @@ -30,48 +29,17 @@ validate_observables, no_sampling, ) -from pennylane.tape import QuantumTape +from pennylane.tape import QuantumTape, QuantumScript from pennylane.transforms.core import TransformProgram from pennylane.typing import Result, ResultBatch -from .device_modifiers import convert_single_circuit_to_batch - -def simulate( - circuit, - rng=None, - c_dtype=np.complex128, - batch_obs=False, - mcmc=False, - kernel_name="Local", - num_burnin=100, -): - """Calculate the results for a given circuit.""" - return 0.0 +from ._state_vector import LightningStateVector +from ._measurements import LightningMeasurements try: - # pylint: disable=import-error, no-name-in-module - from pennylane_lightning.lightning_qubit_ops import ( - StateVectorC64, - StateVectorC128, - ) - -<<<<<<< HEAD -def jacobian(circuit): - """Calculate the jacobian for a given circuit.""" - return np.array(0.0) - + # pylint: disable=import-error, unused-import + import pennylane_lightning.lightning_qubit_ops -def simulate_and_jacobian(circuit): - """Calculate the results and jacobian for a single circuit.""" - return np.array(0.0), np.array(0.0) - - -Result_or_ResultBatch = Union[Result, ResultBatch] -QuantumTapeBatch = Sequence[qml.tape.QuantumTape] -QuantumTape_or_Batch = Union[qml.tape.QuantumTape, QuantumTapeBatch] - - -======= LQ_CPP_BINARY_AVAILABLE = True except ImportError: LQ_CPP_BINARY_AVAILABLE = False @@ -82,31 +50,18 @@ def simulate_and_jacobian(circuit): PostprocessingFn = Callable[[ResultBatch], Result_or_ResultBatch] -<<<<<<< HEAD -def simulate(circuit: QuantumTape, dtype=np.complex128) -> Result: - """Simulate a single quantum script.a - - Args: - circuit (QuantumTape): The single circuit to simulate - dtype: Datatypes for state-vector representation. Must be one of - ``np.complex64`` or ``np.complex128``. -======= def simulate(circuit: QuantumScript, state: LightningStateVector) -> Result: """Simulate a single quantum script. - Args: circuit (QuantumTape): The single circuit to simulate state (LightningStateVector): handle to Lightning state vector ->>>>>>> fde61720 (create state vector on initialization) - Returns: tuple(TensorLike): The results of the simulation - Note that this function can return measurements for non-commuting observables simultaneously. - """ - state = LightningStateVector(num_wires=circuit.num_wires, dtype=dtype).get_final_state(circuit) - return LightningMeasurements(state).measure_final_state(circuit) + state.reset_state() + final_state = state.get_final_state(circuit) + return LightningMeasurements(final_state).measure_final_state(circuit) def jacobian(circuit: QuantumTape): @@ -117,7 +72,6 @@ def simulate_and_jacobian(circuit: QuantumTape): return np.array(0.0), np.array(0.0) ->>>>>>> 2a8cd8da (merge conflicts) _operations = frozenset( { "Identity", @@ -233,49 +187,15 @@ def accepted_observables(obs: qml.operation.Operator) -> bool: return obs.name in _observables -def accepted_analytic_measurements(m: qml.measurements.MeasurementProcess) -> bool: - """Whether or not a state based measurement is supported by ``lightning.qubit``.""" - return isinstance(m, (qml.measurements.ExpectationMP)) - - @simulator_tracking -@convert_single_circuit_to_batch +@single_tape_support class LightningQubit2(Device): - """PennyLane Lightning Qubit device. - - A device that interfaces with C++ to perform fast linear algebra calculations. - - Use of this device requires pre-built binaries or compilation from source. Check out the - :doc:`/lightning_qubit/installation` guide for more details. - - Args: - wires (int): the number of wires to initialize the device with - c_dtype: Datatypes for statevector representation. Must be one of - ``np.complex64`` or ``np.complex128``. - shots (int): How many times the circuit should be evaluated (or sampled) to estimate - the expectation values. Defaults to ``None`` if not specified. Setting - to ``None`` results in computing statistics like expectation values and - variances analytically. - seed (str, int, rng) - mcmc (bool): Determine whether to use the approximate Markov Chain Monte Carlo - sampling method when generating samples. - kernel_name (str): name of transition kernel. The current version supports - two kernels: ``"Local"`` and ``"NonZeroRandom"``. - The local kernel conducts a bit-flip local transition between states. - The local kernel generates a random qubit site and then generates a random - number to determine the new bit at that qubit site. The ``"NonZeroRandom"`` kernel - randomly transits between states that have nonzero probability. - num_burnin (int): number of steps that will be dropped. Increasing this value will - result in a closer approximation but increased runtime. - batch_obs (bool): Determine whether we process observables in parallel when - computing the jacobian. This value is only relevant when the lightning - qubit is built with OpenMP. - """ - - name = "lightning.qubit2" + """PennyLane Lightning Qubit device.""" _device_options = ("rng", "c_dtype", "batch_obs", "mcmc", "kernel_name", "num_burnin") + _CPP_BINARY_AVAILABLE = LQ_CPP_BINARY_AVAILABLE + def __init__( # pylint: disable=too-many-arguments self, wires, @@ -289,9 +209,12 @@ def __init__( # pylint: disable=too-many-arguments batch_obs=False, ): if not LQ_CPP_BINARY_AVAILABLE: - raise ImportError("Pre-compiled binaries for lightning.qubit are not available. " + raise ImportError( + "Pre-compiled binaries for lightning.qubit are not available. " "To manually compile from source, follow the instructions at " - "https://pennylane-lightning.readthedocs.io/en/latest/installation.html.") + "https://pennylane-lightning.readthedocs.io/en/latest/installation.html." + ) + super().__init__(wires=wires, shots=shots) self._statevector = LightningStateVector(num_wires=len(self.wires), dtype=c_dtype) @@ -370,9 +293,7 @@ def supports_derivatives( def preprocess(self, execution_config: ExecutionConfig = DefaultExecutionConfig): program = TransformProgram() - program.add_transform( - validate_measurements, analytic_measurements=accepted_analytic_measurements, name=self.name - ) + program.add_transform(validate_measurements, name=self.name) program.add_transform(no_sampling) program.add_transform(validate_observables, accepted_observables, name=self.name) program.add_transform(validate_device_wires, self.wires, name=self.name) @@ -386,7 +307,6 @@ def execute( circuits: QuantumTape_or_Batch, execution_config: ExecutionConfig = DefaultExecutionConfig, ) -> Result_or_ResultBatch: - results = [] for circuit in circuits: circuit = circuit.map_to_standard_wires() diff --git a/tests/lightning_qubit2/test_expval_2.py b/tests/lightning_qubit2/test_expval_2.py index 78553c3e44..17a1301538 100644 --- a/tests/lightning_qubit2/test_expval_2.py +++ b/tests/lightning_qubit2/test_expval_2.py @@ -34,14 +34,11 @@ VARPHI = np.linspace(0.02, 1, 3) +@pytest.mark.parametrize("c_dtype", (np.complex64, np.complex128)) @pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI))) class TestExpval: """Test expectation value calculations""" - @pytest.fixture(params=[np.complex64, np.complex128]) - def dev(self, request): - return LightningQubit(c_dtype=request.param) - @staticmethod def calculate_reference(tape): dev = DefaultQubit(max_workers=1) @@ -57,53 +54,52 @@ def process_and_execute(dev, tape): results = dev.execute(tapes) return transf_fn(results) - def test_Identity(self, theta, phi, dev, tol): + def test_Identity(self, theta, phi, c_dtype, tol): """Tests applying identities.""" - with qml.tape.QuantumTape() as tape: - qml.Identity(wires=[0]) - qml.Identity(wires=[0, 1]) - qml.Identity(wires=[1, 2]) - qml.RX(theta, wires=[0]) - qml.RX(phi, wires=[1]) - qml.expval(qml.PauliX(0)) - - calculated_val = self.process_and_execute(dev, tape) - reference_val = self.calculate_reference(tape) - + ops = [ + qml.Identity(0), + qml.Identity((0,1)), + qml.Identity((1,2)), + qml.RX(theta, 0), + qml.RX(phi, 1) + ] + measurements = [qml.expval(qml.PauliZ(0))] + tape = qml.tape.QuantumScript(ops, measurements) + + dev = LightningQubit(c_dtype=c_dtype, wires=3) + result = dev.execute(tape) + expected = np.cos(theta) tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 - assert np.allclose(calculated_val, reference_val, tol) + assert np.allclose(result, expected, tol) - def test_identity_expectation(self, theta, phi, dev, tol): - """Tests identity.""" + def test_identity_expectation(self, theta, phi, c_dtype, tol): + """Tests identity expectations.""" + dev = LightningQubit(wires=2, c_dtype=c_dtype) tape = qml.tape.QuantumScript( [qml.RX(theta, wires=[0]), qml.RX(phi, wires=[1]), qml.CNOT(wires=[0, 1])], [qml.expval(qml.Identity(wires=[0])), qml.expval(qml.Identity(wires=[1]))], ) - - calculated_val = self.process_and_execute(dev, tape) - reference_val = self.calculate_reference(tape) + result = dev.execute(tape) tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 - assert np.allclose(calculated_val, reference_val, tol) + assert np.allclose(1.0, result, tol) - def test_multi_wire_identity_expectation(self, theta, phi, dev, tol): + def test_multi_wire_identity_expectation(self, theta, phi, c_dtype, tol): """Tests multi-wire identity.""" + dev = LightningQubit(wires=2, c_dtype=c_dtype) tape = qml.tape.QuantumScript( [qml.RX(theta, wires=[0]), qml.RX(phi, wires=[1]), qml.CNOT(wires=[0, 1])], [qml.expval(qml.Identity(wires=[0, 1]))], ) - - calculated_val = self.process_and_execute(dev, tape) - reference_val = self.calculate_reference(tape) - + result = dev.execute(tape) tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 - assert np.allclose(calculated_val, reference_val, tol) + assert np.allclose(1.0, result, tol) @pytest.mark.parametrize( "wires", @@ -113,9 +109,10 @@ def test_multi_wire_identity_expectation(self, theta, phi, dev, tol): (["b", "a"]), ], ) - def test_PauliZ_expectation(self, theta, phi, dev, tol, wires): + def test_PauliZ_expectation(self, theta, phi, c_dtype, tol, wires): """Tests PauliZ.""" + dev = LightningQubit(wires=wires, c_dtype=c_dtype) tape = qml.tape.QuantumScript( [qml.RX(theta, wires=wires[0]), qml.RX(phi, wires=wires[1]), qml.CNOT(wires=wires)], [qml.expval(qml.PauliZ(wires=wires[0])), qml.expval(qml.PauliZ(wires=wires[1]))], From 5bae4ac6d96864b92aeab83f5b16b84234192365 Mon Sep 17 00:00:00 2001 From: albi3ro Date: Wed, 21 Feb 2024 15:36:39 -0500 Subject: [PATCH 177/428] register with setup.py, state vector fixes --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 8c6fbfdf68..df732bdf87 100644 --- a/setup.py +++ b/setup.py @@ -186,6 +186,7 @@ def build_extension(self, ext: CMakeExtension): suffix = suffix[0].upper() + suffix[1:] pennylane_plugins = [device_name + " = pennylane_lightning." + backend + ":Lightning" + suffix] +pennylane_plugins.append("lightning.qubit2 = pennylane_lightning.lightning_qubit.lightning_qubit2:LightningQubit2") pkg_suffix = "" if suffix == "Qubit" else "_" + suffix From 56d76a266ecd7fc71106a908c858854a9bf237ff Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Fri, 23 Feb 2024 13:49:11 -0500 Subject: [PATCH 178/428] add LightningQubit2 to init and format --- pennylane_lightning/lightning_qubit/__init__.py | 1 + tests/lightning_qubit2/test_expval_2.py | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/__init__.py b/pennylane_lightning/lightning_qubit/__init__.py index a1b792afde..017f7fedec 100644 --- a/pennylane_lightning/lightning_qubit/__init__.py +++ b/pennylane_lightning/lightning_qubit/__init__.py @@ -16,3 +16,4 @@ from pennylane_lightning.core import __version__ from .lightning_qubit import LightningQubit +from .lightning_qubit2 import LightningQubit2 diff --git a/tests/lightning_qubit2/test_expval_2.py b/tests/lightning_qubit2/test_expval_2.py index 17a1301538..1bf57af8a1 100644 --- a/tests/lightning_qubit2/test_expval_2.py +++ b/tests/lightning_qubit2/test_expval_2.py @@ -59,10 +59,10 @@ def test_Identity(self, theta, phi, c_dtype, tol): ops = [ qml.Identity(0), - qml.Identity((0,1)), - qml.Identity((1,2)), + qml.Identity((0, 1)), + qml.Identity((1, 2)), qml.RX(theta, 0), - qml.RX(phi, 1) + qml.RX(phi, 1), ] measurements = [qml.expval(qml.PauliZ(0))] tape = qml.tape.QuantumScript(ops, measurements) From 8f85bab62245e2c7a766caf85fd29a39dccda86a Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Wed, 28 Feb 2024 13:53:38 -0500 Subject: [PATCH 179/428] add cpp binary available variable --- pennylane_lightning/lightning_qubit/lightning_qubit2.py | 5 ++++- tests/lightning_qubit2/test_expval_2.py | 6 +++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index 2153a201ae..eb16d41583 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -208,7 +208,10 @@ def __init__( # pylint: disable=too-many-arguments num_burnin=100, batch_obs=False, ): - if not LQ_CPP_BINARY_AVAILABLE: + if LQ_CPP_BINARY_AVAILABLE: + self._CPP_BINARY_AVAILABLE = True + else: + self._CPP_BINARY_AVAILABLE = False raise ImportError( "Pre-compiled binaries for lightning.qubit are not available. " "To manually compile from source, follow the instructions at " diff --git a/tests/lightning_qubit2/test_expval_2.py b/tests/lightning_qubit2/test_expval_2.py index 1bf57af8a1..abdaf05a6a 100644 --- a/tests/lightning_qubit2/test_expval_2.py +++ b/tests/lightning_qubit2/test_expval_2.py @@ -23,12 +23,12 @@ from conftest import LightningDevice # tested device -if not LightningQubit._CPP_BINARY_AVAILABLE: - pytest.skip("No binary module found. Skipping.", allow_module_level=True) - if LightningDevice != LightningQubit: pytest.skip("Exclusive tests for lightning.qubit. Skipping.", allow_module_level=True) +if not LightningQubit._CPP_BINARY_AVAILABLE: + pytest.skip("No binary module found. Skipping.", allow_module_level=True) + THETA = np.linspace(0.11, 1, 3) PHI = np.linspace(0.32, 1, 3) VARPHI = np.linspace(0.02, 1, 3) From e2929a53ced0982c24d6db26414e0588f0f1bdca Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Thu, 29 Feb 2024 08:29:56 -0500 Subject: [PATCH 180/428] reduce dependency on DefaultQubit for tests --- tests/lightning_qubit2/test_expval_2.py | 120 +++++++++++------------- 1 file changed, 56 insertions(+), 64 deletions(-) diff --git a/tests/lightning_qubit2/test_expval_2.py b/tests/lightning_qubit2/test_expval_2.py index abdaf05a6a..1349e02b94 100644 --- a/tests/lightning_qubit2/test_expval_2.py +++ b/tests/lightning_qubit2/test_expval_2.py @@ -18,7 +18,7 @@ import numpy as np import pennylane as qml -from pennylane_lightning.lightning_qubit import LightningQubit2 as LightningQubit +from pennylane_lightning.lightning_qubit import LightningQubit, LightningQubit2 from pennylane.devices import DefaultQubit from conftest import LightningDevice # tested device @@ -26,7 +26,7 @@ if LightningDevice != LightningQubit: pytest.skip("Exclusive tests for lightning.qubit. Skipping.", allow_module_level=True) -if not LightningQubit._CPP_BINARY_AVAILABLE: +if not LightningQubit2._CPP_BINARY_AVAILABLE: pytest.skip("No binary module found. Skipping.", allow_module_level=True) THETA = np.linspace(0.11, 1, 3) @@ -34,11 +34,14 @@ VARPHI = np.linspace(0.02, 1, 3) -@pytest.mark.parametrize("c_dtype", (np.complex64, np.complex128)) @pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI))) class TestExpval: """Test expectation value calculations""" + @pytest.fixture(params=[np.complex64, np.complex128]) + def dev(self, request): + return LightningQubit2(wires=3, c_dtype=request.param) + @staticmethod def calculate_reference(tape): dev = DefaultQubit(max_workers=1) @@ -54,7 +57,7 @@ def process_and_execute(dev, tape): results = dev.execute(tapes) return transf_fn(results) - def test_Identity(self, theta, phi, c_dtype, tol): + def test_Identity(self, theta, phi, dev, tol): """Tests applying identities.""" ops = [ @@ -67,17 +70,15 @@ def test_Identity(self, theta, phi, c_dtype, tol): measurements = [qml.expval(qml.PauliZ(0))] tape = qml.tape.QuantumScript(ops, measurements) - dev = LightningQubit(c_dtype=c_dtype, wires=3) result = dev.execute(tape) expected = np.cos(theta) tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 assert np.allclose(result, expected, tol) - def test_identity_expectation(self, theta, phi, c_dtype, tol): + def test_identity_expectation(self, theta, phi, dev, tol): """Tests identity expectations.""" - dev = LightningQubit(wires=2, c_dtype=c_dtype) tape = qml.tape.QuantumScript( [qml.RX(theta, wires=[0]), qml.RX(phi, wires=[1]), qml.CNOT(wires=[0, 1])], [qml.expval(qml.Identity(wires=[0])), qml.expval(qml.Identity(wires=[1]))], @@ -88,10 +89,9 @@ def test_identity_expectation(self, theta, phi, c_dtype, tol): assert np.allclose(1.0, result, tol) - def test_multi_wire_identity_expectation(self, theta, phi, c_dtype, tol): + def test_multi_wire_identity_expectation(self, theta, phi, dev, tol): """Tests multi-wire identity.""" - dev = LightningQubit(wires=2, c_dtype=c_dtype) tape = qml.tape.QuantumScript( [qml.RX(theta, wires=[0]), qml.RX(phi, wires=[1]), qml.CNOT(wires=[0, 1])], [qml.expval(qml.Identity(wires=[0, 1]))], @@ -109,68 +109,66 @@ def test_multi_wire_identity_expectation(self, theta, phi, c_dtype, tol): (["b", "a"]), ], ) - def test_PauliZ_expectation(self, theta, phi, c_dtype, tol, wires): - """Tests PauliZ.""" + def test_custom_wires(self, theta, phi, tol, wires): + """Tests custom wires.""" + dev = LightningQubit2(wires=wires) - dev = LightningQubit(wires=wires, c_dtype=c_dtype) tape = qml.tape.QuantumScript( [qml.RX(theta, wires=wires[0]), qml.RX(phi, wires=wires[1]), qml.CNOT(wires=wires)], [qml.expval(qml.PauliZ(wires=wires[0])), qml.expval(qml.PauliZ(wires=wires[1]))], ) calculated_val = self.process_and_execute(dev, tape) - reference_val = self.calculate_reference(tape) - - tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 - - assert np.allclose(calculated_val, reference_val, tol) - - def test_PauliX_expectation(self, theta, phi, dev, tol): - """Tests PauliX.""" - - tape = qml.tape.QuantumScript( - [qml.RY(theta, wires=[0]), qml.RY(phi, wires=[1]), qml.CNOT(wires=[0, 1])], - [qml.expval(qml.PauliX(wires=[0])), qml.expval(qml.PauliX(wires=[1]))], - ) - - calculated_val = self.process_and_execute(dev, tape) - reference_val = self.calculate_reference(tape) - - tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 - - assert np.allclose(calculated_val, reference_val, tol) - - def test_PauliY_expectation(self, theta, phi, dev, tol): - """Tests PauliY.""" - - tape = qml.tape.QuantumScript( - [qml.RX(theta, wires=[0]), qml.RX(phi, wires=[1]), qml.CNOT(wires=[0, 1])], - [qml.expval(qml.PauliY(wires=[0])), qml.expval(qml.PauliY(wires=[1]))], - ) - - calculated_val = self.process_and_execute(dev, tape) - reference_val = self.calculate_reference(tape) + reference_val = np.array([np.cos(theta), np.cos(theta) * np.cos(phi)]) tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 assert np.allclose(calculated_val, reference_val, tol) - def test_hadamard_expectation(self, theta, phi, dev, tol): - """Tests Hadamard.""" + @pytest.mark.parametrize( + "Obs, Op, expected_fn", + [ + ( + [qml.PauliX(wires=[0]), qml.PauliX(wires=[1])], + qml.RY, + lambda theta, phi: np.array([np.sin(theta) * np.sin(phi), np.sin(phi)]), + ), + ( + [qml.PauliY(wires=[0]), qml.PauliY(wires=[1])], + qml.RX, + lambda theta, phi: np.array([0, -np.cos(theta) * np.sin(phi)]), + ), + ( + [qml.PauliZ(wires=[0]), qml.PauliZ(wires=[1])], + qml.RX, + lambda theta, phi: np.array([np.cos(theta), np.cos(theta) * np.cos(phi)]), + ), + ( + [qml.Hadamard(wires=[0]), qml.Hadamard(wires=[1])], + qml.RY, + lambda theta, phi: np.array( + [ + np.sin(theta) * np.sin(phi) + np.cos(theta), + np.cos(theta) * np.cos(phi) + np.sin(phi), + ] + ) + / np.sqrt(2), + ), + ], + ) + def test_single_wire_observables_expectation(self, Obs, Op, expected_fn, theta, phi, tol, dev): + """Test that expectation values for single wire observables are correct""" tape = qml.tape.QuantumScript( - [qml.RY(theta, wires=[0]), qml.RY(phi, wires=[1]), qml.CNOT(wires=[0, 1])], - [qml.expval(qml.Hadamard(wires=[0])), qml.expval(qml.Hadamard(wires=[1]))], + [Op(theta, wires=[0]), Op(phi, wires=[1]), qml.CNOT(wires=[0, 1])], + [qml.expval(Obs[0]), qml.expval(Obs[1])], ) + result = self.process_and_execute(dev, tape) + expected = expected_fn(theta, phi) - calculated_val = self.process_and_execute(dev, tape) - reference_val = self.calculate_reference(tape) - - tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 - - assert np.allclose(calculated_val, reference_val, tol) + assert np.allclose(result, expected, tol) - def test_hermitian_expectation(self, theta, phi, dev, tol): + def test_hermitian_expectation(self, theta, phi, tol, dev): """Tests an Hermitian operator.""" with qml.tape.QuantumTape() as tape: @@ -184,11 +182,9 @@ def test_hermitian_expectation(self, theta, phi, dev, tol): calculated_val = self.process_and_execute(dev, tape) reference_val = self.calculate_reference(tape) - tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 - assert np.allclose(calculated_val, reference_val, tol) - def test_hamiltonian_expectation(self, theta, phi, dev, tol): + def test_hamiltonian_expectation(self, theta, phi, tol, dev): """Tests a Hamiltonian.""" ham = qml.Hamiltonian( @@ -211,11 +207,9 @@ def test_hamiltonian_expectation(self, theta, phi, dev, tol): calculated_val = self.process_and_execute(dev, tape) reference_val = self.calculate_reference(tape) - tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 - assert np.allclose(calculated_val, reference_val, tol) - def test_sparse_hamiltonian_expectation(self, theta, phi, dev, tol): + def test_sparse_hamiltonian_expectation(self, theta, phi, tol, dev): """Tests a Hamiltonian.""" ham = qml.SparseHamiltonian( @@ -240,8 +234,6 @@ def test_sparse_hamiltonian_expectation(self, theta, phi, dev, tol): calculated_val = self.process_and_execute(dev, tape) reference_val = self.calculate_reference(tape) - tol = 1e-5 if dev.c_dtype == np.complex64 else 1e-7 - assert np.allclose(calculated_val, reference_val, tol) @@ -251,7 +243,7 @@ class TestOperatorArithmetic: @pytest.fixture(params=[np.complex64, np.complex128]) def dev(self, request): - return LightningQubit(c_dtype=request.param) + return LightningQubit2(wires=2, c_dtype=request.param) @staticmethod def calculate_reference(tape): @@ -336,7 +328,7 @@ class TestTensorExpval: @pytest.fixture(params=[np.complex64, np.complex128]) def dev(self, request): - return LightningQubit(c_dtype=request.param) + return LightningQubit2(wires=3, c_dtype=request.param) @staticmethod def calculate_reference(tape): From b38c242994e858514d896600c49907b29fdd2544 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Thu, 29 Feb 2024 08:31:43 -0500 Subject: [PATCH 181/428] update LightningQubit2 --- pennylane_lightning/lightning_qubit/lightning_qubit2.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index eb16d41583..2153a201ae 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -208,10 +208,7 @@ def __init__( # pylint: disable=too-many-arguments num_burnin=100, batch_obs=False, ): - if LQ_CPP_BINARY_AVAILABLE: - self._CPP_BINARY_AVAILABLE = True - else: - self._CPP_BINARY_AVAILABLE = False + if not LQ_CPP_BINARY_AVAILABLE: raise ImportError( "Pre-compiled binaries for lightning.qubit are not available. " "To manually compile from source, follow the instructions at " From 94808440f3691543a550a18c1029922de38cf989 Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Wed, 6 Mar 2024 10:39:01 -0500 Subject: [PATCH 182/428] Fixing rebase artifacts --- setup.py | 7 +- tests/conftest.py | 15 +- tests/lightning_qubit2/test_serialize_2.py | 563 --------------------- 3 files changed, 8 insertions(+), 577 deletions(-) delete mode 100644 tests/lightning_qubit2/test_serialize_2.py diff --git a/setup.py b/setup.py index df732bdf87..977f753559 100644 --- a/setup.py +++ b/setup.py @@ -186,7 +186,6 @@ def build_extension(self, ext: CMakeExtension): suffix = suffix[0].upper() + suffix[1:] pennylane_plugins = [device_name + " = pennylane_lightning." + backend + ":Lightning" + suffix] -pennylane_plugins.append("lightning.qubit2 = pennylane_lightning.lightning_qubit.lightning_qubit2:LightningQubit2") pkg_suffix = "" if suffix == "Qubit" else "_" + suffix @@ -204,9 +203,9 @@ def build_extension(self, ext: CMakeExtension): "long_description": open("README.rst").read(), "long_description_content_type": "text/x-rst", "install_requires": requirements, - "ext_modules": [] - if os.environ.get("SKIP_COMPILATION", False) - else [CMakeExtension(f"{backend}_ops")], + "ext_modules": ( + [] if os.environ.get("SKIP_COMPILATION", False) else [CMakeExtension(f"{backend}_ops")] + ), "cmdclass": {"build_ext": CMakeBuild}, "ext_package": "pennylane_lightning", "extras_require": { diff --git a/tests/conftest.py b/tests/conftest.py index dd1fecc795..96531bf421 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -87,7 +87,7 @@ def n_subsystems(request): # Looking for the device for testing. default_device = "lightning.qubit" -supported_devices = {"lightning.kokkos", "lightning.qubit", "lightning.qubit2", "lightning.gpu"} +supported_devices = {"lightning.kokkos", "lightning.qubit", "lightning.gpu"} supported_devices.update({sb.replace(".", "_") for sb in supported_devices}) @@ -113,10 +113,10 @@ def get_device(): device_name = get_device() -# if device_name not in qml.plugin_devices: -# raise qml.DeviceError( -# f"Device {device_name} does not exist. Make sure the required plugin is installed." -# ) +if device_name not in qml.plugin_devices: + raise qml.DeviceError( + f"Device {device_name} does not exist. Make sure the required plugin is installed." + ) # Device specification import pennylane_lightning.lightning_qubit as lightning_ops # Any definition of lightning_ops will do @@ -131,11 +131,6 @@ def get_device(): if hasattr(pennylane_lightning, "lightning_gpu_ops"): import pennylane_lightning.lightning_gpu_ops as lightning_ops -elif device_name == "lightning.qubit2": - from pennylane_lightning.lightning_qubit import LightningQubit2 as LightningDevice - - if hasattr(pennylane_lightning, "lightning_qubit_ops"): - import pennylane_lightning.lightning_qubit_ops as lightning_ops else: from pennylane_lightning.lightning_qubit import LightningQubit as LightningDevice diff --git a/tests/lightning_qubit2/test_serialize_2.py b/tests/lightning_qubit2/test_serialize_2.py deleted file mode 100644 index 7f14422c38..0000000000 --- a/tests/lightning_qubit2/test_serialize_2.py +++ /dev/null @@ -1,563 +0,0 @@ -# Copyright 2018-2024 Xanadu Quantum Technologies Inc. - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -""" -Unit tests for the serialization helper functions. -""" -import pytest -from conftest import device_name, LightningDevice - -import numpy as np -import pennylane as qml -from pennylane_lightning.lightning_qubit._serialize import QuantumScriptSerializer - -# from pennylane_lightning.lightning_qubit._serialize_old import QuantumScriptSerializer - -if not LightningDevice._CPP_BINARY_AVAILABLE: - pytest.skip("No binary module found. Skipping.", allow_module_level=True) - -if device_name == "lightning.kokkos": - from pennylane_lightning.lightning_kokkos_ops.observables import ( - NamedObsC64, - NamedObsC128, - HermitianObsC64, - HermitianObsC128, - TensorProdObsC64, - TensorProdObsC128, - HamiltonianC64, - HamiltonianC128, - SparseHamiltonianC64, - SparseHamiltonianC128, - ) -elif device_name == "lightning.gpu": - from pennylane_lightning.lightning_gpu_ops.observables import ( - NamedObsC64, - NamedObsC128, - HermitianObsC64, - HermitianObsC128, - TensorProdObsC64, - TensorProdObsC128, - HamiltonianC64, - HamiltonianC128, - SparseHamiltonianC64, - SparseHamiltonianC128, - ) -else: - from pennylane_lightning.lightning_qubit_ops.observables import ( - NamedObsC64, - NamedObsC128, - HermitianObsC64, - HermitianObsC128, - TensorProdObsC64, - TensorProdObsC128, - HamiltonianC64, - HamiltonianC128, - SparseHamiltonianC64, - SparseHamiltonianC128, - ) - - -def test_wrong_device_name(): - """Test the device name is not a valid option""" - - with pytest.raises(qml.DeviceError, match="The device name"): - QuantumScriptSerializer("thunder.qubit") - - -@pytest.mark.parametrize( - "obs,obs_type", - [ - (qml.PauliZ(0), NamedObsC128), - (qml.PauliZ(0) @ qml.PauliZ(1), TensorProdObsC128), - (qml.Hadamard(0), NamedObsC128), - (qml.Hermitian(np.eye(2), wires=0), HermitianObsC128), - ( - qml.PauliZ(0) @ qml.Hadamard(1) @ (0.1 * (qml.PauliZ(2) + qml.PauliX(3))), - HamiltonianC128, - ), - ( - ( - qml.Hermitian(np.eye(2), wires=0) - @ qml.Hermitian(np.eye(2), wires=1) - @ qml.Projector([0], wires=2) - ), - TensorProdObsC128, - ), - ( - qml.PauliZ(0) @ qml.Hermitian(np.eye(2), wires=1) @ qml.Projector([0], wires=2), - TensorProdObsC128, - ), - (qml.Projector([0], wires=0), HermitianObsC128), - (qml.Hamiltonian([1], [qml.PauliZ(0)]), HamiltonianC128), - (qml.sum(qml.Hadamard(0), qml.PauliX(1)), HermitianObsC128), - ( - qml.SparseHamiltonian(qml.Hamiltonian([1], [qml.PauliZ(0)]).sparse_matrix(), wires=[0]), - SparseHamiltonianC128, - ), - ], -) -def test_obs_returns_expected_type(obs, obs_type): - """Tests that observables get serialized to the expected type.""" - assert isinstance(QuantumScriptSerializer(device_name)._ob(obs), obs_type) - - -class TestSerializeObs: - """Tests for the serialize_observables function""" - - @pytest.mark.parametrize("use_csingle", [True, False]) - def test_tensor_non_tensor_return(self, use_csingle): - """Test expected serialization for a mixture of tensor product and non-tensor product - return""" - with qml.tape.QuantumTape() as tape: - qml.expval(qml.PauliZ(0) @ qml.PauliX(1)) - qml.expval(qml.Hadamard(1)) - - tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 - named_obs = NamedObsC64 if use_csingle else NamedObsC128 - - s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) - - s_expected = [ - tensor_prod_obs([named_obs("PauliZ", [0]), named_obs("PauliX", [1])]), - named_obs("Hadamard", [1]), - ] - - assert s == s_expected - - @pytest.mark.parametrize("use_csingle", [True, False]) - def test_hermitian_return(self, use_csingle): - """Test expected serialization for a Hermitian return""" - with qml.tape.QuantumTape() as tape: - qml.expval(qml.Hermitian(np.eye(4), wires=[0, 1])) - - hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 - c_dtype = np.complex64 if use_csingle else np.complex128 - - s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) - s_expected = hermitian_obs( - np.array( - [1.0, 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, 1.0], - dtype=c_dtype, - ), - [0, 1], - ) - assert s[0] == s_expected - - @pytest.mark.parametrize("use_csingle", [True, False]) - def test_hermitian_tensor_return(self, use_csingle): - """Test expected serialization for a Hermitian return""" - with qml.tape.QuantumTape() as tape: - qml.expval(qml.Hermitian(np.eye(4), wires=[0, 1]) @ qml.Hermitian(np.eye(2), wires=[2])) - - c_dtype = np.complex64 if use_csingle else np.complex128 - tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 - hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 - s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) - - s_expected = tensor_prod_obs( - [ - hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), - hermitian_obs(np.eye(2, dtype=c_dtype).ravel(), [2]), - ] - ) - - assert s[0] == s_expected - - @pytest.mark.parametrize("use_csingle", [True, False]) - def test_mixed_tensor_return(self, use_csingle): - """Test expected serialization for a mixture of Hermitian and Pauli return""" - with qml.tape.QuantumTape() as tape: - qml.expval(qml.Hermitian(np.eye(4), wires=[0, 1]) @ qml.PauliY(2)) - - c_dtype = np.complex64 if use_csingle else np.complex128 - tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 - hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 - named_obs = NamedObsC64 if use_csingle else NamedObsC128 - - s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) - - s_expected = tensor_prod_obs( - [hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), named_obs("PauliY", [2])] - ) - - assert s[0] == s_expected - - @pytest.mark.parametrize("use_csingle", [True, False]) - def test_hamiltonian_return(self, use_csingle): - """Test expected serialization for a Hamiltonian return""" - - ham = qml.Hamiltonian( - [0.3, 0.5, 0.4], - [ - qml.Hermitian(np.eye(4), wires=[0, 1]) @ qml.PauliY(2), - qml.PauliX(0) @ qml.PauliY(2), - qml.Hermitian(np.ones((8, 8)), wires=range(3)), - ], - ) - - with qml.tape.QuantumTape() as tape: - qml.expval(ham) - - hamiltonian_obs = HamiltonianC64 if use_csingle else HamiltonianC128 - named_obs = NamedObsC64 if use_csingle else NamedObsC128 - hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 - tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 - r_dtype = np.float32 if use_csingle else np.float64 - c_dtype = np.complex64 if use_csingle else np.complex128 - - s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) - - s_expected = hamiltonian_obs( - np.array([0.3, 0.5, 0.4], dtype=r_dtype), - [ - tensor_prod_obs( - [ - hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), - named_obs("PauliY", [2]), - ] - ), - tensor_prod_obs([named_obs("PauliX", [0]), named_obs("PauliY", [2])]), - hermitian_obs(np.ones(64, dtype=c_dtype), [0, 1, 2]), - ], - ) - - assert s[0] == s_expected - - @pytest.mark.parametrize("use_csingle", [True, False]) - def test_hamiltonian_tensor_return(self, use_csingle): - """Test expected serialization for a Hamiltonian return""" - - with qml.tape.QuantumTape() as tape: - ham = qml.Hamiltonian( - [0.3, 0.5, 0.4], - [ - qml.Hermitian(np.eye(4), wires=[0, 1]) @ qml.PauliY(2), - qml.PauliX(0) @ qml.PauliY(2), - qml.Hermitian(np.ones((8, 8)), wires=range(3)), - ], - ) - qml.expval(ham @ qml.PauliZ(3)) - - hamiltonian_obs = HamiltonianC64 if use_csingle else HamiltonianC128 - named_obs = NamedObsC64 if use_csingle else NamedObsC128 - hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 - tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 - r_dtype = np.float32 if use_csingle else np.float64 - c_dtype = np.complex64 if use_csingle else np.complex128 - - s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) - - # Expression (ham @ obs) is converted internally by Pennylane - # where obs is appended to each term of the ham - s_expected = hamiltonian_obs( - np.array([0.3, 0.5, 0.4], dtype=r_dtype), - [ - tensor_prod_obs( - [ - hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), - named_obs("PauliY", [2]), - named_obs("PauliZ", [3]), - ] - ), - tensor_prod_obs( - [named_obs("PauliX", [0]), named_obs("PauliY", [2]), named_obs("PauliZ", [3])] - ), - tensor_prod_obs( - [hermitian_obs(np.ones(64, dtype=c_dtype), [0, 1, 2]), named_obs("PauliZ", [3])] - ), - ], - ) - - assert s[0] == s_expected - - @pytest.mark.parametrize("use_csingle", [True, False]) - def test_hamiltonian_mix_return(self, use_csingle): - """Test expected serialization for a Hamiltonian return""" - - ham1 = qml.Hamiltonian( - [0.3, 0.5, 0.4], - [ - qml.Hermitian(np.eye(4), wires=[0, 1]) @ qml.PauliY(2), - qml.PauliX(0) @ qml.PauliY(2), - qml.Hermitian(np.ones((8, 8)), wires=range(3)), - ], - ) - ham2 = qml.Hamiltonian( - [0.7, 0.3], - [qml.PauliX(0) @ qml.Hermitian(np.eye(4), wires=[1, 2]), qml.PauliY(0) @ qml.PauliX(2)], - ) - - with qml.tape.QuantumTape() as tape: - qml.expval(ham1) - qml.expval(ham2) - - hamiltonian_obs = HamiltonianC64 if use_csingle else HamiltonianC128 - named_obs = NamedObsC64 if use_csingle else NamedObsC128 - hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 - tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 - r_dtype = np.float32 if use_csingle else np.float64 - c_dtype = np.complex64 if use_csingle else np.complex128 - - s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) - - s_expected1 = hamiltonian_obs( - np.array([0.3, 0.5, 0.4], dtype=r_dtype), - [ - tensor_prod_obs( - [ - hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), - named_obs("PauliY", [2]), - ] - ), - tensor_prod_obs([named_obs("PauliX", [0]), named_obs("PauliY", [2])]), - hermitian_obs(np.ones(64, dtype=c_dtype), [0, 1, 2]), - ], - ) - s_expected2 = hamiltonian_obs( - np.array([0.7, 0.3], dtype=r_dtype), - [ - tensor_prod_obs( - [ - named_obs("PauliX", [0]), - hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [1, 2]), - ] - ), - tensor_prod_obs([named_obs("PauliY", [0]), named_obs("PauliX", [2])]), - ], - ) - - assert s[0] == s_expected1 - assert s[1] == s_expected2 - - @pytest.mark.parametrize( - "obs,coeffs,terms", - [ - (qml.prod(qml.PauliZ(0), qml.PauliX(1)), [1], [[("PauliX", 1), ("PauliZ", 0)]]), - (qml.s_prod(0.1, qml.PauliX(0)), [0.1], ("PauliX", 0)), - ( - qml.sum( - 0.5 * qml.prod(qml.PauliX(0), qml.PauliZ(1)), - 0.1 * qml.prod(qml.PauliZ(0), qml.PauliY(1)), - ), - [0.5, 0.1], - [[("PauliZ", 1), ("PauliX", 0)], [("PauliY", 1), ("PauliZ", 0)]], - ), - ], - ) - @pytest.mark.parametrize("use_csingle", [True, False]) - def test_op_arithmetic_uses_hamiltonian(self, use_csingle, obs, coeffs, terms): - """Tests that an arithmetic obs with a PauliRep serializes as a Hamiltonian.""" - tape = qml.tape.QuantumTape(measurements=[qml.expval(obs)]) - res, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) - assert len(res) == 1 - assert isinstance(res[0], HamiltonianC64 if use_csingle else HamiltonianC128) - - hamiltonian_obs = HamiltonianC64 if use_csingle else HamiltonianC128 - tensor_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 - named_obs = NamedObsC64 if use_csingle else NamedObsC128 - rtype = np.float32 if use_csingle else np.float64 - term_shape = np.array(terms).shape - - if len(term_shape) == 1: # just a single pauli op - expected_terms = [named_obs(terms[0], [terms[1]])] - elif len(term_shape) == 3: # list of tensor products - expected_terms = [ - tensor_obs([named_obs(pauli, [wire]) for pauli, wire in term]) for term in terms - ] - - coeffs = np.array(coeffs).astype(rtype) - assert res[0] == hamiltonian_obs(coeffs, expected_terms) - - @pytest.mark.parametrize("use_csingle", [True, False]) - def test_multi_wire_identity(self, use_csingle): - """Tests that multi-wire Identity does not fail serialization.""" - tape = qml.tape.QuantumTape(measurements=[qml.expval(qml.Identity(wires=[1, 2]))]) - res, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables(tape) - assert len(res) == 1 - - named_obs = NamedObsC64 if use_csingle else NamedObsC128 - assert res[0] == named_obs("Identity", [1]) - - -class TestSerializeOps: - """Tests for the _ops function""" - - def test_basic_circuit(self): - """Test expected serialization for a simple circuit""" - with qml.tape.QuantumTape() as tape: - qml.RX(0.4, wires=0) - qml.RY(0.6, wires=1) - qml.CNOT(wires=[0, 1]) - - s = QuantumScriptSerializer(device_name).serialize_ops(tape) - s_expected = ( - ( - ["RX", "RY", "CNOT"], - [np.array([0.4]), np.array([0.6]), []], - [[0], [1], [0, 1]], - [False, False, False], - [[], [], []], - [[], [], []], - [[], [], []], - ), - False, - ) - assert s == s_expected - - def test_basic_circuit_not_implemented_ctrl_ops(self): - """Test expected serialization for a simple circuit""" - ops = qml.OrbitalRotation(0.1234, wires=range(4)) - with qml.tape.QuantumTape() as tape: - qml.RX(0.4, wires=0) - qml.RY(0.6, wires=1) - qml.ctrl(ops, [4, 5]) - - s = QuantumScriptSerializer(device_name).serialize_ops(tape) - s_expected = ( - ( - ["RX", "RY", "QubitUnitary"], - [np.array([0.4]), np.array([0.6]), [0.0]], - [[0], [1], list(ops.wires)], - [False, False, False], - [[], [], [qml.matrix(ops)]], - [[], [], [4, 5]], - ), - False, - ) - assert s[0][0] == s_expected[0][0] - assert s[0][1] == s_expected[0][1] - assert s[0][2] == s_expected[0][2] - assert s[0][3] == s_expected[0][3] - assert all(np.allclose(s0, s1) for s0, s1 in zip(s[0][4], s_expected[0][4])) - assert s[0][5] == s_expected[0][5] - assert s[1] == s_expected[1] - - def test_multicontrolledx(self): - """Test expected serialization for a simple circuit""" - with qml.tape.QuantumTape() as tape: - qml.RX(0.4, wires=0) - qml.RY(0.6, wires=1) - qml.ctrl(qml.PauliX(wires=0), [1, 2, 3], control_values=[True, False, False]) - - s = QuantumScriptSerializer(device_name).serialize_ops(tape) - s_expected = ( - ( - ["RX", "RY", "PauliX"], - [np.array([0.4]), np.array([0.6]), []], - [[0], [1], [0]], - [False, False, False], - [[], [], []], - [[], [], [1, 2, 3]], - [[], [], [True, False, False]], - ), - False, - ) - assert s == s_expected - - @pytest.mark.parametrize("stateprep", [qml.QubitStateVector, qml.StatePrep]) - def test_skips_prep_circuit(self, stateprep): - """Test expected serialization for a simple circuit with state preparation, such that - the state preparation is skipped""" - with qml.tape.QuantumTape() as tape: - stateprep([1, 0], wires=0) - qml.BasisState([1], wires=1) - qml.RX(0.4, wires=0) - qml.RY(0.6, wires=1) - qml.CNOT(wires=[0, 1]) - - s = QuantumScriptSerializer(device_name).serialize_ops(tape) - s_expected = ( - ( - ["RX", "RY", "CNOT"], - [[0.4], [0.6], []], - [[0], [1], [0, 1]], - [False, False, False], - [[], [], []], - [[], [], []], - [[], [], []], - ), - True, - ) - assert s == s_expected - - def test_unsupported_kernel_circuit(self): - """Test expected serialization for a circuit including gates that do not have a dedicated - kernel""" - with qml.tape.QuantumTape() as tape: - qml.CNOT(wires=[0, 1]) - qml.RZ(0.2, wires=2) - - s = QuantumScriptSerializer(device_name).serialize_ops(tape) - s_expected = ( - ( - ["CNOT", "RZ"], - [[], [0.2]], - [[0, 1], [2]], - [False, False], - ), - False, - ) - assert s[0][0] == s_expected[0][0] - assert s[0][1] == s_expected[0][1] - - @pytest.mark.parametrize("C", [True, False]) - def test_integration(self, C): - """Test expected serialization for a random circuit""" - with qml.tape.QuantumTape() as tape: - qml.RX(0.4, wires=0) - qml.RY(0.6, wires=1) - qml.CNOT(wires=[0, 1]) - qml.QubitUnitary(np.eye(4), wires=[0, 1]) - qml.templates.QFT(wires=[0, 1, 2]) - qml.DoubleExcitation(0.555, wires=[3, 2, 1, 0]) - qml.DoubleExcitationMinus(0.555, wires=[0, 1, 2, 3]) - qml.DoubleExcitationPlus(0.555, wires=[0, 1, 2, 3]) - - s = QuantumScriptSerializer(device_name).serialize_ops(tape) - - dtype = np.complex64 if C else np.complex128 - s_expected = ( - ( - [ - "RX", - "RY", - "CNOT", - "QubitUnitary", - "QFT", - "DoubleExcitation", - "DoubleExcitationMinus", - "DoubleExcitationPlus", - ], - [[0.4], [0.6], [], [0.0], [], [0.555], [0.555], [0.555]], - [[0], [1], [0, 1], [0, 1], [0, 1, 2], [3, 2, 1, 0], [0, 1, 2, 3], [0, 1, 2, 3]], - [False, False, False, False, False, False, False, False], - [ - [], - [], - [], - qml.matrix(qml.QubitUnitary(np.eye(4, dtype=dtype), wires=[0, 1])), - qml.matrix(qml.templates.QFT(wires=[0, 1, 2])), - [], - [], - [], - ], - ), - False, - ) - assert s[0][0] == s_expected[0][0] - assert s[0][1] == s_expected[0][1] - assert s[0][2] == s_expected[0][2] - assert s[0][3] == s_expected[0][3] - assert s[1] == s_expected[1] - - assert all(np.allclose(s1, s2) for s1, s2 in zip(s[0][4], s_expected[0][4])) From 76fb7d9dea19e50ce2cd06874deee43b790379f7 Mon Sep 17 00:00:00 2001 From: albi3ro Date: Wed, 6 Mar 2024 12:06:58 -0500 Subject: [PATCH 183/428] remove adjoint diff support from supports derivatives --- .../lightning_qubit/lightning_qubit2.py | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index 2153a201ae..c2b3028640 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -280,16 +280,19 @@ def supports_derivatives( execution_config: Optional[ExecutionConfig] = None, circuit: Optional[qml.tape.QuantumTape] = None, ) -> bool: - if execution_config is None and circuit is None: - return True - if execution_config.gradient_method not in {"adjoint", "best"}: - return False - if circuit is None: - return True - return ( - all(isinstance(m, qml.measurements.ExpectationMP) for m in circuit.measurements) - and not circuit.shots - ) + if False: + # to be used once adjoint differentiation support is added. + if execution_config is None and circuit is None: + return True + if execution_config.gradient_method not in {"adjoint", "best"}: + return False + if circuit is None: + return True + return ( + all(isinstance(m, qml.measurements.ExpectationMP) for m in circuit.measurements) + and not circuit.shots + ) + return False def preprocess(self, execution_config: ExecutionConfig = DefaultExecutionConfig): program = TransformProgram() From c9f72d0b8326a3e71a290af711861885d727c1f1 Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Thu, 7 Mar 2024 10:36:44 -0500 Subject: [PATCH 184/428] [skip ci] Added skeleton file for LQ2 unit tests --- .../lightning_qubit2/test_lightning_qubit2.py | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 tests/lightning_qubit2/test_lightning_qubit2.py diff --git a/tests/lightning_qubit2/test_lightning_qubit2.py b/tests/lightning_qubit2/test_lightning_qubit2.py new file mode 100644 index 0000000000..4b040ce2e3 --- /dev/null +++ b/tests/lightning_qubit2/test_lightning_qubit2.py @@ -0,0 +1,71 @@ +# Copyright 2018-2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This module contains unit tests for the LightningQubit2 class +""" + +import pytest + +import numpy as np +import pennylane as qml +from pennylane_lightning.lightning_qubit import LightningQubit, LightningQubit2 +from pennylane_lightning.lightning_qubit.lightning_qubit2 import ( + simulate, + jacobian, + simulate_and_jacobian, +) +from pennylane.devices import DefaultQubit + +from conftest import LightningDevice # tested device + +if LightningDevice != LightningQubit: + pytest.skip("Exclusive tests for lightning.qubit. Skipping.", allow_module_level=True) + +if not LightningQubit2._CPP_BINARY_AVAILABLE: + pytest.skip("No binary module found. Skipping.", allow_module_level=True) + +THETA = np.linspace(0.11, 1, 3) +PHI = np.linspace(0.32, 1, 3) +VARPHI = np.linspace(0.02, 1, 3) + + +class TestHelpers: + """Unit tests for the simulate function""" + + # Test simulate + # Test jacobian + xfail tests + # Test simulate_and_jacobian + xfail tests + # Test stopping_condition + # Test accepted_observables + + +class TestInitialization: + """Unit tests for LightningQubit2 initialization""" + + # Test __init__ errors: invalid num_burnin, kernel name + + +class TestExecution: + """Unit tests for executing quantum tapes on LightningQubit2""" + + # Test preprocess + # Test execute + + +class TestDerivatives: + """Unit tests for calculating derivatives with LightningQubit2""" + + # Test supports derivative + xfail tests + # Test compute_derivatives + xfail tests + # Test execute_and_compute_derivatives + xfail tests From 86ade7b3c31a4730eb1522084364c0d876131749 Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Thu, 7 Mar 2024 11:44:49 -0500 Subject: [PATCH 185/428] Lightning qubit2 upgrade api (#628) * update * adding tests from add-simulate branch * merge conflicts * create state vector on initialization * remove import of modifier from lightning * Update pennylane_lightning/lightning_qubit/lightning_qubit2.py * minor test updates * register with setup.py, state vector fixes * add LightningQubit2 to init and format * add cpp binary available variable * Auto update version * reduce dependency on DefaultQubit for tests * update LightningQubit2 * Introduce _new_API and fix/skip few tests. * Fix few more tests. * Skip shots, adjoint, vjp with new API. * Fix no-bin interface. * Remove duplicate class data. * Include LQ2 in linux ests. * --cov-append --------- Co-authored-by: albi3ro Co-authored-by: AmintorDusko Co-authored-by: Dev version update bot --- .github/workflows/tests_linux.yml | 1 + pennylane_lightning/core/_serialize.py | 2 +- pennylane_lightning/core/lightning_base.py | 6 ++- .../lightning_gpu/lightning_gpu.py | 2 +- .../lightning_qubit/lightning_qubit2.py | 48 +++++++++++++------ pennylane_lightning/lightning_qubit2 | 1 + setup.py | 1 + tests/conftest.py | 7 ++- .../test_measurements_class.py | 17 +++++-- tests/lightning_qubit2/test_expval_2.py | 7 ++- tests/test_adjoint_jacobian.py | 3 ++ tests/test_apply.py | 47 ++++++++++++++---- tests/test_decomposition.py | 1 + tests/test_device.py | 2 +- tests/test_expval.py | 12 ++++- tests/test_gates.py | 9 ++-- tests/test_measurements.py | 17 +++++-- tests/test_var.py | 4 +- tests/test_vjp.py | 3 ++ 19 files changed, 142 insertions(+), 48 deletions(-) create mode 120000 pennylane_lightning/lightning_qubit2 diff --git a/.github/workflows/tests_linux.yml b/.github/workflows/tests_linux.yml index 91bd44bb85..b334c0acce 100644 --- a/.github/workflows/tests_linux.yml +++ b/.github/workflows/tests_linux.yml @@ -185,6 +185,7 @@ jobs: cd main/ DEVICENAME=`echo ${{ matrix.pl_backend }} | sed "s/_/./g"` PL_DEVICE=${DEVICENAME} python -m pytest tests/ $COVERAGE_FLAGS + PL_DEVICE=${DEVICENAME}2 python -m pytest tests/ $COVERAGE_FLAGS --cov-append pl-device-test --device ${DEVICENAME} --skip-ops --shots=20000 $COVERAGE_FLAGS --cov-append pl-device-test --device ${DEVICENAME} --shots=None --skip-ops $COVERAGE_FLAGS --cov-append mv .coverage .coverage-${{ github.job }}-${{ matrix.pl_backend }} diff --git a/pennylane_lightning/core/_serialize.py b/pennylane_lightning/core/_serialize.py index 2c85333b05..2eb0078aa5 100644 --- a/pennylane_lightning/core/_serialize.py +++ b/pennylane_lightning/core/_serialize.py @@ -60,7 +60,7 @@ def __init__( self.use_csingle = use_csingle self.device_name = device_name self.split_obs = split_obs - if device_name == "lightning.qubit": + if device_name in ("lightning.qubit", "lightning.qubit2"): try: import pennylane_lightning.lightning_qubit_ops as lightning_ops except ImportError as exception: diff --git a/pennylane_lightning/core/lightning_base.py b/pennylane_lightning/core/lightning_base.py index 36fa4c504f..88f2d78030 100644 --- a/pennylane_lightning/core/lightning_base.py +++ b/pennylane_lightning/core/lightning_base.py @@ -60,6 +60,7 @@ class LightningBase(QubitDevice): author = "Xanadu Inc." short_name = "lightning.base" _CPP_BINARY_AVAILABLE = True + _new_API = False def __init__( self, @@ -76,7 +77,7 @@ def __init__( r_dtype = np.float64 self.use_csingle = False else: - raise TypeError(f"Unsupported complex Type: {c_dtype}") + raise TypeError(f"Unsupported complex type: {c_dtype}") super().__init__(wires, shots=shots, r_dtype=r_dtype, c_dtype=c_dtype) self._batch_obs = batch_obs @@ -396,6 +397,7 @@ class LightningBaseFallBack(DefaultQubitLegacy): # pragma: no cover version = __version__ author = "Xanadu Inc." _CPP_BINARY_AVAILABLE = False + _new_API = False def __init__(self, wires, *, c_dtype=np.complex128, **kwargs): if c_dtype is np.complex64: @@ -403,7 +405,7 @@ def __init__(self, wires, *, c_dtype=np.complex128, **kwargs): elif c_dtype is np.complex128: r_dtype = np.float64 else: - raise TypeError(f"Unsupported complex Type: {c_dtype}") + raise TypeError(f"Unsupported complex type: {c_dtype}") super().__init__(wires, r_dtype=r_dtype, c_dtype=c_dtype, **kwargs) @property diff --git a/pennylane_lightning/lightning_gpu/lightning_gpu.py b/pennylane_lightning/lightning_gpu/lightning_gpu.py index be36ff548d..1a5a45798b 100644 --- a/pennylane_lightning/lightning_gpu/lightning_gpu.py +++ b/pennylane_lightning/lightning_gpu/lightning_gpu.py @@ -261,7 +261,7 @@ def __init__( elif c_dtype is np.complex128: self.use_csingle = False else: - raise TypeError(f"Unsupported complex Type: {c_dtype}") + raise TypeError(f"Unsupported complex type: {c_dtype}") super().__init__(wires, shots=shots, c_dtype=c_dtype) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index c2b3028640..10684e595a 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -13,32 +13,33 @@ # limitations under the License. """ This module contains the LightningQubit2 class that inherits from the new device interface. + """ -from typing import Optional, Union, Sequence, Callable from dataclasses import replace -import numpy as np +from pathlib import Path +from typing import Callable, Optional, Sequence, Union +import numpy as np import pennylane as qml -from pennylane.devices import Device, ExecutionConfig, DefaultExecutionConfig -from pennylane.devices.modifiers import single_tape_support, simulator_tracking +from pennylane.devices import DefaultExecutionConfig, Device, ExecutionConfig +from pennylane.devices.modifiers import simulator_tracking, single_tape_support from pennylane.devices.preprocess import ( decompose, + no_sampling, validate_device_wires, - decompose, validate_measurements, validate_observables, - no_sampling, ) -from pennylane.tape import QuantumTape, QuantumScript +from pennylane.tape import QuantumScript, QuantumTape from pennylane.transforms.core import TransformProgram from pennylane.typing import Result, ResultBatch -from ._state_vector import LightningStateVector from ._measurements import LightningMeasurements +from ._state_vector import LightningStateVector try: # pylint: disable=import-error, unused-import - import pennylane_lightning.lightning_qubit_ops + from pennylane_lightning.lightning_qubit_ops import backend_info LQ_CPP_BINARY_AVAILABLE = True except ImportError: @@ -52,12 +53,16 @@ def simulate(circuit: QuantumScript, state: LightningStateVector) -> Result: """Simulate a single quantum script. + Args: circuit (QuantumTape): The single circuit to simulate state (LightningStateVector): handle to Lightning state vector + Returns: tuple(TensorLike): The results of the simulation + Note that this function can return measurements for non-commuting observables simultaneously. + """ state.reset_state() final_state = state.get_final_state(circuit) @@ -153,6 +158,8 @@ def simulate_and_jacobian(circuit: QuantumTape): "QFT", "ECR", "BlockEncode", + "GlobalPhase", + "C(GlobalPhase)", } ) """The set of supported operations.""" @@ -194,7 +201,13 @@ class LightningQubit2(Device): _device_options = ("rng", "c_dtype", "batch_obs", "mcmc", "kernel_name", "num_burnin") + _new_API = True _CPP_BINARY_AVAILABLE = LQ_CPP_BINARY_AVAILABLE + short_name = "lightning.qubit2" + operations = _operations + observables = _observables + _backend_info = backend_info if LQ_CPP_BINARY_AVAILABLE else None + config = Path(__file__).parent / "lightning_qubit.toml" def __init__( # pylint: disable=too-many-arguments self, @@ -247,14 +260,19 @@ def c_dtype(self): return self._c_dtype @property - def operations(self) -> frozenset[str]: - """The names of the supported operations.""" - return _operations + def C_DTYPE(self): + """State vector complex data type.""" + return self._c_dtype + + @property + def num_wires(self): + """State vector complex data type.""" + return self._statevector.num_wires @property - def observables(self) -> frozenset[str]: - """The names of the supported observables.""" - return _observables + def state(self): + """Returns a copy of the state vector data in a NumPy array.""" + return self._statevector.state def _setup_execution_config(self, config): """ diff --git a/pennylane_lightning/lightning_qubit2 b/pennylane_lightning/lightning_qubit2 new file mode 120000 index 0000000000..0bcda1f466 --- /dev/null +++ b/pennylane_lightning/lightning_qubit2 @@ -0,0 +1 @@ +lightning_qubit \ No newline at end of file diff --git a/setup.py b/setup.py index 977f753559..5e37d8e2b2 100644 --- a/setup.py +++ b/setup.py @@ -186,6 +186,7 @@ def build_extension(self, ext: CMakeExtension): suffix = suffix[0].upper() + suffix[1:] pennylane_plugins = [device_name + " = pennylane_lightning." + backend + ":Lightning" + suffix] +pennylane_plugins.append("lightning.qubit2 = pennylane_lightning.lightning_qubit.lightning_qubit2:LightningQubit2") pkg_suffix = "" if suffix == "Qubit" else "_" + suffix diff --git a/tests/conftest.py b/tests/conftest.py index 96531bf421..cbc0d29dde 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -87,7 +87,7 @@ def n_subsystems(request): # Looking for the device for testing. default_device = "lightning.qubit" -supported_devices = {"lightning.kokkos", "lightning.qubit", "lightning.gpu"} +supported_devices = {"lightning.kokkos", "lightning.qubit", "lightning.qubit2", "lightning.gpu"} supported_devices.update({sb.replace(".", "_") for sb in supported_devices}) @@ -131,6 +131,11 @@ def get_device(): if hasattr(pennylane_lightning, "lightning_gpu_ops"): import pennylane_lightning.lightning_gpu_ops as lightning_ops +elif device_name == "lightning.qubit2": + from pennylane_lightning.lightning_qubit2 import LightningQubit2 as LightningDevice + + if hasattr(pennylane_lightning, "lightning_qubit_ops"): + import pennylane_lightning.lightning_qubit_ops as lightning_ops else: from pennylane_lightning.lightning_qubit import LightningQubit as LightningDevice diff --git a/tests/lightning_qubit/test_measurements_class.py b/tests/lightning_qubit/test_measurements_class.py index 42b589a089..b25f71e871 100644 --- a/tests/lightning_qubit/test_measurements_class.py +++ b/tests/lightning_qubit/test_measurements_class.py @@ -21,19 +21,27 @@ import numpy as np import pennylane as qml import pytest -from conftest import LightningDevice # tested device +from conftest import LightningDevice, device_name # tested device from pennylane.devices import DefaultQubit from pennylane.measurements import VarianceMP from scipy.sparse import csr_matrix, random_array -from pennylane_lightning.lightning_qubit import LightningQubit +try: + from pennylane_lightning.lightning_qubit_ops import ( + MeasurementsC64, + MeasurementsC128, + ) +except ImportError: + pass + +from pennylane_lightning.lightning_qubit2 import LightningQubit2 from pennylane_lightning.lightning_qubit._measurements import LightningMeasurements from pennylane_lightning.lightning_qubit._state_vector import LightningStateVector -if not LightningQubit._CPP_BINARY_AVAILABLE: +if not LightningDevice._CPP_BINARY_AVAILABLE: pytest.skip("No binary module found. Skipping.", allow_module_level=True) -if LightningDevice != LightningQubit: +if LightningDevice != LightningQubit2: pytest.skip("Exclusive tests for lightning.qubit. Skipping.", allow_module_level=True) THETA = np.linspace(0.11, 1, 3) @@ -422,6 +430,7 @@ def calculate_reference(tape, lightning_sv): ( [0], [1, 2], + [1, 0], qml.PauliX(0), qml.PauliY(1), qml.PauliZ(2), diff --git a/tests/lightning_qubit2/test_expval_2.py b/tests/lightning_qubit2/test_expval_2.py index 1349e02b94..6952c49e95 100644 --- a/tests/lightning_qubit2/test_expval_2.py +++ b/tests/lightning_qubit2/test_expval_2.py @@ -14,14 +14,13 @@ """ Tests for process and execute (expval calculation). """ -import pytest - import numpy as np import pennylane as qml -from pennylane_lightning.lightning_qubit import LightningQubit, LightningQubit2 +import pytest +from conftest import LightningDevice # tested device from pennylane.devices import DefaultQubit -from conftest import LightningDevice # tested device +from pennylane_lightning.lightning_qubit import LightningQubit, LightningQubit2 if LightningDevice != LightningQubit: pytest.skip("Exclusive tests for lightning.qubit. Skipping.", allow_module_level=True) diff --git a/tests/test_adjoint_jacobian.py b/tests/test_adjoint_jacobian.py index ad9db67239..df73ec46ca 100644 --- a/tests/test_adjoint_jacobian.py +++ b/tests/test_adjoint_jacobian.py @@ -26,6 +26,9 @@ from pennylane import qchem, qnode from scipy.stats import unitary_group +if ld._new_API: + pytest.skip("Old API required", allow_module_level=True) + I, X, Y, Z = ( np.eye(2), qml.PauliX.compute_matrix(), diff --git a/tests/test_apply.py b/tests/test_apply.py index 5af7d4101a..8aae0e0553 100644 --- a/tests/test_apply.py +++ b/tests/test_apply.py @@ -30,6 +30,7 @@ from pennylane.wires import Wires +@pytest.mark.skipif(ld._new_API, reason="Old API required") class TestApply: """Tests that operations of certain operations are applied correctly or that the proper errors are raised. @@ -532,6 +533,7 @@ def test_apply_state_vector_lightning_handle(self, qubit_device, tol): class TestExpval: """Tests that expectation values are properly calculated or that the proper errors are raised.""" + @pytest.mark.skipif(ld._new_API, reason="Old API required") @pytest.mark.parametrize( "operation,input,expected_output", [ @@ -566,6 +568,7 @@ def test_expval_single_wire_no_parameters( assert np.isclose(res, expected_output, atol=tol, rtol=0) + @pytest.mark.xfail(ld._new_API, reason="Old API required") def test_expval_estimate(self): """Test that the expectation value is not analytically calculated""" dev = qml.device(device_name, wires=1, shots=3) @@ -584,6 +587,7 @@ def circuit(): class TestVar: """Tests that variances are properly calculated.""" + @pytest.mark.skipif(ld._new_API, reason="Old API required") @pytest.mark.parametrize( "operation,input,expected_output", [ @@ -618,6 +622,7 @@ def test_var_single_wire_no_parameters( assert np.isclose(res, expected_output, atol=tol, rtol=0) + @pytest.mark.xfail(ld._new_API, reason="Old API required") def test_var_estimate(self): """Test that the variance is not analytically calculated""" @@ -637,6 +642,7 @@ def circuit(): class TestSample: """Tests that samples are properly calculated.""" + @pytest.mark.skipif(ld._new_API, reason="Old API required") def test_sample_dimensions(self, qubit_device): """Tests if the samples returned by the sample function have the correct dimensions @@ -670,6 +676,7 @@ def test_sample_dimensions(self, qubit_device): s3 = dev.sample(qml.PauliX(0) @ qml.PauliZ(1)) assert np.array_equal(s3.shape, (17,)) + @pytest.mark.skipif(ld._new_API, reason="Old API required") def test_sample_values(self, qubit_device, tol): """Tests if the samples returned by sample have the correct values @@ -697,6 +704,7 @@ class TestLightningDeviceIntegration: """Integration tests for lightning device. This test ensures it integrates properly with the PennyLane interface, in particular QNode.""" + @pytest.mark.xfail(ld._new_API, reason="Old API required") def test_load_default_qubit_device(self): """Test that the default plugin loads correctly""" @@ -705,6 +713,7 @@ def test_load_default_qubit_device(self): assert dev.shots is None assert dev.short_name == device_name + @pytest.mark.xfail(ld._new_API, reason="Old API required") @pytest.mark.skipif(not ld._CPP_BINARY_AVAILABLE, reason="Lightning binary required") def test_no_backprop(self): """Test that lightning device does not support the backprop @@ -719,6 +728,7 @@ def circuit(): with pytest.raises(qml.QuantumFunctionError): qml.QNode(circuit, dev, diff_method="backprop") + @pytest.mark.xfail(ld._new_API, reason="Old API required") @pytest.mark.skipif(not ld._CPP_BINARY_AVAILABLE, reason="Lightning binary required") def test_best_gets_lightning(self): """Test that the best differentiation method returns lightning @@ -767,6 +777,7 @@ def circuit(x): assert np.isclose(circuit(p), 1, atol=tol, rtol=0) + @pytest.mark.xfail(ld._new_API, reason="Old API required") def test_nonzero_shots(self, tol_stochastic): """Test that the default qubit plugin provides correct result for high shot number""" @@ -804,7 +815,8 @@ def test_supported_gate_single_wire_no_parameters( dev = qubit_device(wires=1) op = getattr(qml.ops, name) - assert dev.supports_operation(name) + if hasattr(dev, "supports_operation"): + assert dev.supports_operation(name) @qml.qnode(dev) def circuit(): @@ -830,7 +842,8 @@ def test_supported_gate_two_wires_no_parameters( dev = qubit_device(wires=2) op = getattr(qml.ops, name) - assert dev.supports_operation(name) + if hasattr(dev, "supports_operation"): + assert dev.supports_operation(name) @qml.qnode(dev) def circuit(): @@ -853,7 +866,8 @@ def test_supported_gate_three_wires_no_parameters( dev = qubit_device(wires=3) op = getattr(qml.ops, name) - assert dev.supports_operation(name) + if hasattr(dev, "supports_operation"): + assert dev.supports_operation(name) @qml.qnode(dev) def circuit(): @@ -880,7 +894,8 @@ def test_supported_state_preparation(self, qubit_device, tol, name, par, expecte dev = qubit_device(wires=2) op = getattr(qml.ops, name) - assert dev.supports_operation(name) + if hasattr(dev, "supports_operation"): + assert dev.supports_operation(name) @qml.qnode(dev) def circuit(): @@ -998,7 +1013,8 @@ def test_supported_gate_single_wire_with_parameters( dev = qubit_device(wires=1) op = getattr(qml.ops, name) - assert dev.supports_operation(name) + if hasattr(dev, "supports_operation"): + assert dev.supports_operation(name) @qml.qnode(dev) def circuit(): @@ -1040,7 +1056,8 @@ def test_supported_gate_two_wires_with_parameters( dev = qubit_device(wires=2) op = getattr(qml.ops, name) - assert dev.supports_operation(name) + if hasattr(dev, "supports_operation"): + assert dev.supports_operation(name) @qml.qnode(dev) def circuit(): @@ -1075,7 +1092,8 @@ def test_supported_observable_single_wire_no_parameters( dev = qubit_device(wires=1) obs = getattr(qml.ops, name) - assert dev.supports_observable(name) + if hasattr(dev, "supports_observable"): + assert dev.supports_observable(name) @qml.qnode(dev) def circuit(): @@ -1100,7 +1118,8 @@ def test_supported_observable_single_wire_with_parameters( dev = qubit_device(wires=1) obs = getattr(qml.ops, name) - assert dev.supports_observable(name) + if hasattr(dev, "supports_observable"): + assert dev.supports_observable(name) @qml.qnode(dev) def circuit(): @@ -1109,6 +1128,7 @@ def circuit(): assert np.isclose(circuit(), expected_output, atol=tol, rtol=0) + @pytest.mark.xfail(ld._new_API, reason="Old API required") def test_multi_samples_return_correlated_results(self, qubit_device): """Tests if the samples returned by the sample function have the correct dimensions @@ -1127,6 +1147,7 @@ def circuit(): assert np.array_equal(outcomes[0], outcomes[1]) @pytest.mark.parametrize("num_wires", [3, 4, 5, 6, 7, 8]) + @pytest.mark.xfail(ld._new_API, reason="Old API required") def test_multi_samples_return_correlated_results_more_wires_than_size_of_observable( self, num_wires ): @@ -1146,6 +1167,7 @@ def circuit(): assert np.array_equal(outcomes[0], outcomes[1]) + @pytest.mark.xfail(ld._new_API, reason="Old API required") def test_snapshot_is_ignored_without_shot(self): """Tests if the Snapshot operator is ignored correctly""" dev = qml.device(device_name, wires=4) @@ -1162,6 +1184,7 @@ def circuit(): assert np.allclose(outcomes, [0.0]) + @pytest.mark.xfail(ld._new_API, reason="Old API required") def test_snapshot_is_ignored_with_shots(self): """Tests if the Snapshot operator is ignored correctly""" dev = qml.device(device_name, wires=4, shots=1000) @@ -1188,10 +1211,13 @@ def circuit(): qml.QuantumPhaseEstimation(qml.matrix(qml.Hadamard)(wires=0), [0], [1]) return qml.probs(wires=[0, 1]) - circuit() + probs = circuit() res_sv = dev.state - res_probs = dev.probability([0, 1]) + if ld._new_API: + res_probs = probs + else: + res_probs = dev.probability([0, 1]) expected_sv = np.array( [ @@ -1210,6 +1236,7 @@ def circuit(): class TestApplyLightningMethod: """Unit tests for the apply_lightning method.""" + @pytest.mark.skipif(ld._new_API, reason="Old API required") def test_apply_identity_skipped(self, mocker, tol): """Test identity operation does not perform additional computations.""" dev = qml.device(device_name, wires=1) diff --git a/tests/test_decomposition.py b/tests/test_decomposition.py index 29a7874871..df9de740e2 100644 --- a/tests/test_decomposition.py +++ b/tests/test_decomposition.py @@ -23,6 +23,7 @@ pytest.skip("No binary module found. Skipping.", allow_module_level=True) +@pytest.mark.skipif(ld._new_API, reason="Old API required") class TestDenseMatrixDecompositionThreshold: """Tests, for QFT and Grover operators, the automatic transition from full matrix to decomposition on calculations.""" diff --git a/tests/test_device.py b/tests/test_device.py index 98691b6fd1..11945e89aa 100644 --- a/tests/test_device.py +++ b/tests/test_device.py @@ -37,7 +37,7 @@ def test_create_device_with_dtype(C): not hasattr(np, "complex256"), reason="Numpy only defines complex256 in Linux-like system" ) def test_create_device_with_unsupported_dtype(): - with pytest.raises(TypeError, match="Unsupported complex Type:"): + with pytest.raises(TypeError, match="Unsupported complex type:"): dev = qml.device(device_name, wires=1, c_dtype=np.complex256) diff --git a/tests/test_expval.py b/tests/test_expval.py index 95a985c362..d1b27f9658 100644 --- a/tests/test_expval.py +++ b/tests/test_expval.py @@ -19,9 +19,10 @@ import numpy as np import pennylane as qml import pytest -from conftest import PHI, THETA, VARPHI, device_name +from conftest import PHI, THETA, VARPHI, LightningDevice, device_name +@pytest.mark.skipif(LightningDevice._new_API, reason="Old API required") @pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI))) class TestExpval: """Test expectation values""" @@ -169,6 +170,8 @@ def test_sprod(self, diff_method, qubit_device): """Test the `SProd` class with lightning qubit.""" dev = qubit_device(wires=2) + if diff_method == "adjoint" and dev.short_name == "lightning.qubit2": + return @qml.qnode(dev, diff_method=diff_method) def circuit(x): @@ -187,6 +190,8 @@ def test_prod(self, diff_method, qubit_device): """Test the `Prod` class with lightning qubit.""" dev = qubit_device(wires=2) + if diff_method == "adjoint" and dev.short_name == "lightning.qubit2": + return @qml.qnode(dev, diff_method=diff_method) def circuit(x): @@ -207,6 +212,8 @@ def test_sum(self, diff_method, qubit_device): """Test the `Sum` class with lightning qubit.""" dev = qubit_device(wires=2) + if diff_method == "adjoint" and dev.short_name == "lightning.qubit2": + return @qml.qnode(dev, diff_method=diff_method) def circuit(x, y): @@ -229,6 +236,8 @@ def test_integration(self, diff_method, qubit_device): obs = qml.sum(qml.s_prod(2.3, qml.PauliZ(0)), -0.5 * qml.prod(qml.PauliY(0), qml.PauliZ(1))) dev = qubit_device(wires=2) + if diff_method == "adjoint" and dev.short_name == "lightning.qubit2": + return @qml.qnode(dev, diff_method=diff_method) def circuit(x, y): @@ -248,6 +257,7 @@ def circuit(x, y): assert qml.math.allclose(g, expected) +@pytest.mark.skipif(LightningDevice._new_API, reason="Old API required") @pytest.mark.parametrize("theta,phi,varphi", list(zip(THETA, PHI, VARPHI))) class TestTensorExpval: """Test tensor expectation values""" diff --git a/tests/test_gates.py b/tests/test_gates.py index 847c3a845a..231993989d 100644 --- a/tests/test_gates.py +++ b/tests/test_gates.py @@ -244,6 +244,7 @@ def output(input): assert np.allclose(unitary, random_unitary_inv) +@pytest.mark.skipif(LightningDevice._new_API, reason="Old API required") @pytest.mark.skipif(not LightningDevice._CPP_BINARY_AVAILABLE, reason="Lightning binary required") @pytest.mark.parametrize( "obs,has_rotation", @@ -320,7 +321,7 @@ def circuit(): @pytest.mark.skipif( - device_name != "lightning.qubit", + device_name not in ("lightning.qubit", "lightning.qubit2"), reason="N-controlled operations only implemented in lightning.qubit.", ) @pytest.mark.parametrize("control_value", [False, True]) @@ -363,7 +364,7 @@ def circuit(): @pytest.mark.skipif( - device_name != "lightning.qubit", + device_name not in ("lightning.qubit", "lightning.qubit2"), reason="N-controlled operations only implemented in lightning.qubit.", ) @pytest.mark.parametrize( @@ -440,7 +441,7 @@ def circuit(): @pytest.mark.skipif( - device_name != "lightning.qubit", + device_name not in ("lightning.qubit", "lightning.qubit2"), reason="N-controlled operations only implemented in lightning.qubit.", ) def test_controlled_qubit_unitary_from_op(tol): @@ -461,7 +462,7 @@ def circuit(x): @pytest.mark.skipif( - device_name != "lightning.qubit", + device_name not in ("lightning.qubit", "lightning.qubit2"), reason="N-controlled operations only implemented in lightning.qubit.", ) @pytest.mark.parametrize("control_wires", range(4)) diff --git a/tests/test_measurements.py b/tests/test_measurements.py index 62960f92fd..ea57a50f8c 100644 --- a/tests/test_measurements.py +++ b/tests/test_measurements.py @@ -27,6 +27,7 @@ pytest.skip("No binary module found. Skipping.", allow_module_level=True) +@pytest.mark.skipif(ld._new_API, reason="Old API required") def test_measurements(): dev = qml.device(device_name, wires=2) m = dev.measurements @@ -54,6 +55,7 @@ class TestProbs: def dev(self, request): return qml.device(device_name, wires=2, c_dtype=request.param) + @pytest.mark.skipif(ld._new_API, reason="Old API required") def test_probs_dtype64(self, dev): """Test if probs changes the state dtype""" _state = dev._asarray( @@ -119,6 +121,7 @@ def circuit(): assert np.allclose(circuit(), cases[1], atol=tol, rtol=0) + @pytest.mark.skipif(ld._new_API, reason="Old API required") @pytest.mark.parametrize( "cases", [ @@ -198,6 +201,7 @@ def circuit(): assert np.allclose(circuit(), cases[1], atol=tol, rtol=0) + @pytest.mark.skipif(ld._new_API, reason="Old API required") @pytest.mark.parametrize( "cases", [ @@ -235,6 +239,7 @@ class TestExpval: def dev(self, request): return qml.device(device_name, wires=2, c_dtype=request.param) + @pytest.mark.skipif(ld._new_API, reason="Old API required") def test_expval_dtype64(self, dev): """Test if expval changes the state dtype""" _state = np.array([1, 0, 0, 0]).astype(dev.C_DTYPE) @@ -349,7 +354,7 @@ def circuit(): qml.RX(0.52, wires=0) return qml.expval(qml.RX(0.742, wires=[0])) - with pytest.raises(qml._device.DeviceError, match="Observable RX not supported"): + with pytest.raises(qml._device.DeviceError, match="Observable RX.*not supported"): circuit() def test_observable_return_type_is_expectation(self, dev): @@ -371,6 +376,7 @@ class TestVar: def dev(self, request): return qml.device(device_name, wires=2, c_dtype=request.param) + @pytest.mark.skipif(ld._new_API, reason="Old API required") def test_var_dtype64(self, dev): """Test if var changes the state dtype""" _state = np.array([1, 0, 0, 0]).astype(np.complex64) @@ -449,7 +455,7 @@ def circuit(): qml.RX(0.52, wires=0) return qml.var(qml.RX(0.742, wires=[0])) - with pytest.raises(qml._device.DeviceError, match="Observable RX not supported"): + with pytest.raises(qml._device.DeviceError, match="Observable RX.*not supported"): circuit() def test_observable_return_type_is_variance(self, dev): @@ -478,13 +484,14 @@ def circuit(): qml.RX(0.52, wires=0) return qml.var(qml.RX(0.742, wires=[0])) - with pytest.raises(qml._device.DeviceError, match="Observable RX not supported"): + with pytest.raises(qml._device.DeviceError, match="Observable RX.*not supported"): circuit() class TestWiresInExpval: """Test different Wires settings in Lightning's expval.""" + @pytest.mark.skipif(ld._new_API, reason="Old API required") @pytest.mark.parametrize( "wires1, wires2", [ @@ -529,6 +536,7 @@ def circuit2(): assert np.allclose(circuit1(), circuit2(), atol=tol) + @pytest.mark.skipif(ld._new_API, reason="Old API required") @pytest.mark.parametrize( "wires1, wires2", [ @@ -585,6 +593,7 @@ def circuit2(): class TestSample: """Tests that samples are properly calculated.""" + @pytest.mark.skipif(ld._new_API, reason="Old API required") @pytest.mark.parametrize( "shots, wires", [ @@ -607,6 +616,7 @@ def test_sample_dimensions(self, qubit_device, shots, wires): s1 = dev.sample(qml.PauliZ(wires=[0])) assert np.array_equal(s1.shape, (dev.shots,)) + @pytest.mark.skipif(ld._new_API, reason="Old API required") def test_sample_values(self, qubit_device, tol): """Tests if the samples returned by sample have the correct values @@ -628,6 +638,7 @@ def test_sample_values(self, qubit_device, tol): class TestWiresInVar: """Test different Wires settings in Lightning's var.""" + @pytest.mark.skipif(ld._new_API, reason="Old API required") @pytest.mark.parametrize( "wires1, wires2", [ diff --git a/tests/test_var.py b/tests/test_var.py index bf0779da6f..fc73a4d748 100644 --- a/tests/test_var.py +++ b/tests/test_var.py @@ -17,7 +17,7 @@ import numpy as np import pennylane as qml import pytest -from conftest import PHI, THETA, VARPHI +from conftest import PHI, THETA, VARPHI, LightningDevice np.random.seed(42) @@ -26,6 +26,7 @@ class TestVar: """Tests for the variance""" + @pytest.mark.skipif(LightningDevice._new_API, reason="Old API required") def test_var(self, theta, phi, qubit_device, tol): """Tests for variance calculation""" dev = qubit_device(wires=3) @@ -71,6 +72,7 @@ def circuit(): assert np.allclose(circ(), circ_def(), tol) +@pytest.mark.skipif(LightningDevice._new_API, reason="Old API required") @pytest.mark.parametrize("theta, phi, varphi", list(zip(THETA, PHI, VARPHI))) class TestTensorVar: """Tests for variance of tensor observables""" diff --git a/tests/test_vjp.py b/tests/test_vjp.py index d53b9b4135..70bd091bf9 100644 --- a/tests/test_vjp.py +++ b/tests/test_vjp.py @@ -25,6 +25,9 @@ if not ld._CPP_BINARY_AVAILABLE: pytest.skip("No binary module found. Skipping.", allow_module_level=True) +if ld._new_API: + pytest.skip("Old API required", allow_module_level=True) + class TestVectorJacobianProduct: """Tests for the `vjp` function""" From 6d134d6366fd843560855e528dc7cb6793948ef9 Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Thu, 7 Mar 2024 12:13:53 -0500 Subject: [PATCH 186/428] Added init tests; Added skeleton tests for helpers --- .../lightning_qubit2/test_lightning_qubit2.py | 110 +++++++++++++++++- 1 file changed, 109 insertions(+), 1 deletion(-) diff --git a/tests/lightning_qubit2/test_lightning_qubit2.py b/tests/lightning_qubit2/test_lightning_qubit2.py index 4b040ce2e3..30e1a941dd 100644 --- a/tests/lightning_qubit2/test_lightning_qubit2.py +++ b/tests/lightning_qubit2/test_lightning_qubit2.py @@ -21,11 +21,16 @@ import pennylane as qml from pennylane_lightning.lightning_qubit import LightningQubit, LightningQubit2 from pennylane_lightning.lightning_qubit.lightning_qubit2 import ( - simulate, + accepted_observables, jacobian, + simulate, simulate_and_jacobian, + stopping_condition, ) +from pennylane_lightning.lightning_qubit._state_vector import LightningStateVector +from pennylane_lightning.lightning_qubit._measurements import LightningMeasurements from pennylane.devices import DefaultQubit +from pennylane.tape import QuantumScript from conftest import LightningDevice # tested device @@ -49,12 +54,115 @@ class TestHelpers: # Test stopping_condition # Test accepted_observables + @pytest.fixture(params=[np.complex64, np.complex128]) + def dev(self, request): + return LightningQubit2(wires=3, c_dtype=request.param) + + @staticmethod + def calculate_reference(tape): + dev = DefaultQubit(max_workers=1) + program, _ = dev.preprocess() + tapes, transf_fn = program([tape]) + results = dev.execute(tapes) + return transf_fn(results) + + @staticmethod + def process_and_execute(dev, tape): + program, _ = dev.preprocess() + tapes, transf_fn = program([tape]) + results = dev.execute(tapes) + return transf_fn(results) + + class DummyOperator(qml.operation.Operation, qml.operation.Observable): + """Dummy operator""" + + num_wires = 1 + + def test_stopping_condition(self): + """Test that stopping_condition returns whether or not an operation + is supported by the device.""" + valid_op = qml.RX(1.23, 0) + invalid_op = self.DummyOperator(0) + + assert stopping_condition(valid_op) is True + assert stopping_condition(invalid_op) is False + + def test_accepted_observables(self): + """Test that accepted_observables returns whether or not an observable + is supported by the device.""" + valid_obs = qml.Projector([0], 0) + invalid_obs = self.DummyOperator(0) + + assert accepted_observables(valid_obs) is True + assert accepted_observables(invalid_obs) is False + + @pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI))) + def test_simulate_single_measurement(self, theta, phi, dev): + """Test that simulate returns the correct results with a single measurement.""" + return + + @pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI))) + def test_simulate_multi_measurement(self, theta, phi, dev): + """Test that simulate returns the correct results with multiple measurements.""" + return + + @pytest.mark.parametrize("theta", THETA) + def test_jacobian_returns_zero(self, theta): + """Test that jacobian always returns zero.""" + tape = QuantumScript([qml.RX(theta, wires=0)], [qml.expval(qml.Z(0))]) + assert np.allclose(jacobian(tape), 0) + + @pytest.mark.xfail + @pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI))) + def test_jacobian_single_expval(self, theta, phi, dev): + """Test that the jacobian is correct when a tape has a single expectation value""" + return + + @pytest.mark.xfail + @pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI))) + def test_jacobian_multi_expval(self, theta, phi, dev): + """Test that the jacobian is correct when a tape has multiple expectation values""" + return + + @pytest.mark.xfail + @pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI))) + def test_simulate_and_jacobian_single_expval(self, theta, phi, dev): + """Test that the result and jacobian is correct when a tape has a single + expectation value""" + return + + @pytest.mark.xfail + @pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI))) + def test_simulate_and_jacobian_multi_expval(self, theta, phi, dev): + """Test that the result and jacobian is correct when a tape has multiple + expectation values""" + return + class TestInitialization: """Unit tests for LightningQubit2 initialization""" # Test __init__ errors: invalid num_burnin, kernel name + def test_invalid_num_burnin_error(self): + """Test that an error is raised when num_burnin is more than number of shots""" + n_shots = 10 + num_burnin = 11 + + with pytest.raises(ValueError, match="Shots should be greater than num_burnin."): + _ = LightningQubit2(wires=2, shots=n_shots, mcmc=True, num_burnin=num_burnin) + + def test_invalid_kernel_name(self): + """Test that an error is raised when the kernel_name is not "Local" or "NonZeroRandom".""" + + _ = LightningQubit2(wires=2, shots=1000, mcmc=True, kernel_name="Local") + _ = LightningQubit2(wires=2, shots=1000, mcmc=True, kernel_name="NonZeroRandom") + + with pytest.raises( + NotImplementedError, match="only 'Local' and 'NonZeroRandom' kernels are supported" + ): + _ = LightningQubit2(wires=2, shots=1000, mcmc=True, kernel_name="bleh") + class TestExecution: """Unit tests for executing quantum tapes on LightningQubit2""" From 573605560016eada0441f48f689fa2d06b351a12 Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Thu, 7 Mar 2024 15:59:56 -0500 Subject: [PATCH 187/428] Resolving rebase artifacts --- .../lightning_kokkos/lightning_kokkos.py | 8 -- .../test_measurements_class.py | 85 ++++--------------- 2 files changed, 17 insertions(+), 76 deletions(-) diff --git a/pennylane_lightning/lightning_kokkos/lightning_kokkos.py b/pennylane_lightning/lightning_kokkos/lightning_kokkos.py index ae9ff2e2f4..4878431445 100644 --- a/pennylane_lightning/lightning_kokkos/lightning_kokkos.py +++ b/pennylane_lightning/lightning_kokkos/lightning_kokkos.py @@ -64,14 +64,6 @@ from pennylane.ops.op_math import Adjoint from pennylane.wires import Wires - # pylint: disable=import-error, no-name-in-module, ungrouped-imports - from pennylane_lightning.lightning_kokkos_ops.algorithms import ( - AdjointJacobianC64, - AdjointJacobianC128, - create_ops_listC64, - create_ops_listC128, - ) - # pylint: disable=import-error, no-name-in-module, ungrouped-imports from pennylane_lightning.core._serialize import ( QuantumScriptSerializer, diff --git a/tests/lightning_qubit/test_measurements_class.py b/tests/lightning_qubit/test_measurements_class.py index b25f71e871..332162d9c1 100644 --- a/tests/lightning_qubit/test_measurements_class.py +++ b/tests/lightning_qubit/test_measurements_class.py @@ -16,8 +16,6 @@ import math from typing import Sequence -from typing import Sequence - import numpy as np import pennylane as qml import pytest @@ -556,77 +554,28 @@ def test_double_return_value(self, measurement, obs0_, obs1_, lightning_sv, tol) for r, e in zip(result, expected): assert np.allclose(r, e, max(tol, 1.0e-5)) - @pytest.mark.parametrize("measurement", [qml.expval, qml.probs, qml.var]) @pytest.mark.parametrize( - "obs0_", - ( - qml.PauliX(0), - qml.PauliY(1), - qml.PauliZ(2), - qml.sum(qml.PauliX(0), qml.PauliY(0)), - qml.prod(qml.PauliX(0), qml.PauliY(1)), - qml.s_prod(2.0, qml.PauliX(0)), - qml.Hermitian(get_hermitian_matrix(2), wires=[0]), - qml.Hermitian(get_hermitian_matrix(2**2), wires=[2, 3]), - qml.Hamiltonian( - [1.0, 2.0, 3.0], [qml.PauliX(0), qml.PauliY(1), qml.PauliZ(2) @ qml.PauliZ(3)] - ), - qml.SparseHamiltonian(get_sparse_hermitian_matrix(2**4), wires=range(4)), - ), - ) - @pytest.mark.parametrize( - "obs1_", - ( - qml.PauliX(0), - qml.PauliY(1), - qml.PauliZ(2), - qml.sum(qml.PauliX(0), qml.PauliY(0)), - qml.prod(qml.PauliX(0), qml.PauliY(1)), - qml.s_prod(2.0, qml.PauliX(0)), - qml.Hermitian(get_hermitian_matrix(2), wires=[0]), - qml.Hermitian(get_hermitian_matrix(2**2), wires=[2, 3]), - qml.Hamiltonian( - [1.0, 2.0, 3.0], [qml.PauliX(0), qml.PauliY(1), qml.PauliZ(2) @ qml.PauliZ(3)] - ), - qml.SparseHamiltonian(get_sparse_hermitian_matrix(2**4), wires=range(4)), - ), + "cases", + [ + [[0, 1], [1, 0]], + [[1, 0], [0, 1]], + ], ) - def test_double_return_value(self, measurement, obs0_, obs1_, lightning_sv, tol): - if measurement is qml.probs and isinstance( - obs0_, - (qml.ops.Sum, qml.ops.SProd, qml.ops.Prod, qml.Hamiltonian, qml.SparseHamiltonian), - ): - return - if measurement is qml.probs and isinstance( - obs1_, - (qml.ops.Sum, qml.ops.SProd, qml.ops.Prod, qml.Hamiltonian, qml.SparseHamiltonian), - ): - pytest.skip( - f"Observable of type {type(obs1_).__name__} is not supported for rotating probabilities." - ) + def test_probs_tape_unordered_wires(self, cases, tol): + """Test probs with a circuit on wires=[0] fails for out-of-order wires passed to probs.""" - n_qubits = 4 - n_layers = 1 - np.random.seed(0) - weights = np.random.rand(n_layers, n_qubits, 3) - ops = [qml.Hadamard(i) for i in range(n_qubits)] - ops += [qml.StronglyEntanglingLayers(weights, wires=range(n_qubits))] - measurements = [measurement(op=obs0_), measurement(op=obs1_)] - tape = qml.tape.QuantumScript(ops, measurements) + x, y, z = [0.5, 0.3, -0.7] + dev = qml.device(device_name, wires=cases[1]) - expected = self.calculate_reference(tape, lightning_sv) - if len(expected) == 1: - expected = expected[0] - statevector = lightning_sv(n_qubits) - statevector = statevector.get_final_state(tape) - m = LightningMeasurements(statevector) - result = m.measure_final_state(tape) + def circuit(): + qml.RX(0.4, wires=[0]) + qml.Rot(x, y, z, wires=[0]) + qml.RY(-0.2, wires=[0]) + return qml.probs(wires=cases[0]) - assert isinstance(result, Sequence) - assert len(result) == len(expected) - # a few tests may fail in single precision, and hence we increase the tolerance - for r, e in zip(result, expected): - assert np.allclose(r, e, max(tol, 1.0e-5)) + expected = qml.QNode(circuit, qml.device("default.qubit", wires=cases[1]))() + results = qml.QNode(circuit, dev)() + assert np.allclose(expected, results, tol) class TestControlledOps: From 283cf5b78b3435e5789e8515ba228e0bad53a09d Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Thu, 7 Mar 2024 21:02:25 +0000 Subject: [PATCH 188/428] Refactor shots test. --- .../lightning_gpu/lightning_gpu.py | 6 +----- requirements-dev.txt | 2 +- tests/lightning_qubit2/test_lightning_qubit2.py | 15 +++++++-------- tests/test_measurements.py | 5 +++-- 4 files changed, 12 insertions(+), 16 deletions(-) diff --git a/pennylane_lightning/lightning_gpu/lightning_gpu.py b/pennylane_lightning/lightning_gpu/lightning_gpu.py index d2345b1360..624ab8fcab 100644 --- a/pennylane_lightning/lightning_gpu/lightning_gpu.py +++ b/pennylane_lightning/lightning_gpu/lightning_gpu.py @@ -509,7 +509,6 @@ def _apply_basis_state(self, state, wires): self._create_basis_state(num) - # pylint: disable=missing-function-docstring def apply_lightning(self, operations): """Apply a list of operations to the state tensor. @@ -641,7 +640,6 @@ def _check_adjdiff_supported_operations(operations): 'the "adjoint" differentiation method' ) - # pylint: disable=missing-function-docstring def _init_process_jacobian_tape(self, tape, starting_state, use_device_state): """Generate an initial state vector for ``_process_jacobian_tape``.""" if starting_state is not None: @@ -866,7 +864,6 @@ def sample(self, observable, shot_range=None, bin_size=None, counts=False): ) return results - # pylint: disable=missing-function-docstring def generate_samples(self): """Generate samples @@ -878,7 +875,7 @@ def generate_samples(self): int, copy=False ) - # pylint: disable=protected-access, missing-function-docstring + # pylint: disable=protected-access def expval(self, observable, shot_range=None, bin_size=None): """Expectation value of the supplied observable. @@ -962,7 +959,6 @@ def probability_lightning(self, wires=None): return local_prob.reshape([2] * num_local_wires).transpose().reshape(-1) return local_prob - # pylint: disable=missing-function-docstring def var(self, observable, shot_range=None, bin_size=None): """Variance of the supplied observable. diff --git a/requirements-dev.txt b/requirements-dev.txt index a09ea5e643..c3808b4d29 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,5 +1,5 @@ pip~=22.0 -git+https://github.com/PennyLaneAI/pennylane.git@qnode-let-device-interpret-best +git+https://github.com/PennyLaneAI/pennylane.git@bugfix/lightning_rotations ninja flaky pybind11 diff --git a/tests/lightning_qubit2/test_lightning_qubit2.py b/tests/lightning_qubit2/test_lightning_qubit2.py index 30e1a941dd..0675c8fbb2 100644 --- a/tests/lightning_qubit2/test_lightning_qubit2.py +++ b/tests/lightning_qubit2/test_lightning_qubit2.py @@ -15,11 +15,16 @@ This module contains unit tests for the LightningQubit2 class """ -import pytest - import numpy as np import pennylane as qml +import pytest +from conftest import LightningDevice # tested device +from pennylane.devices import DefaultQubit +from pennylane.tape import QuantumScript + from pennylane_lightning.lightning_qubit import LightningQubit, LightningQubit2 +from pennylane_lightning.lightning_qubit._measurements import LightningMeasurements +from pennylane_lightning.lightning_qubit._state_vector import LightningStateVector from pennylane_lightning.lightning_qubit.lightning_qubit2 import ( accepted_observables, jacobian, @@ -27,12 +32,6 @@ simulate_and_jacobian, stopping_condition, ) -from pennylane_lightning.lightning_qubit._state_vector import LightningStateVector -from pennylane_lightning.lightning_qubit._measurements import LightningMeasurements -from pennylane.devices import DefaultQubit -from pennylane.tape import QuantumScript - -from conftest import LightningDevice # tested device if LightningDevice != LightningQubit: pytest.skip("Exclusive tests for lightning.qubit. Skipping.", allow_module_level=True) diff --git a/tests/test_measurements.py b/tests/test_measurements.py index 7d9509b6bc..c28a09b755 100644 --- a/tests/test_measurements.py +++ b/tests/test_measurements.py @@ -716,9 +716,10 @@ def test_shots_single_measure_obs(shots, measure_f, obs, mcmc, kernel_name): params = [np.pi / 4, -np.pi / 4] def func(x, y): - qml.Hadamard(0) qml.RX(x, 0) - qml.RX(y, 1) + qml.RX(x, 1) + qml.RZ(y, 0) + qml.RZ(y, 1) return measure_f(wires=obs) if isinstance(obs, Sequence) else measure_f(op=obs) func1 = qml.QNode(func, dev) From 0dd4229b744759ed483e033df6ba59b0b3f919df Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Fri, 8 Mar 2024 14:40:49 -0500 Subject: [PATCH 189/428] Added tests; integrated jacobian --- .../lightning_qubit/lightning_qubit2.py | 115 +++++---- .../lightning_qubit2/test_lightning_qubit2.py | 227 +++++++++++++----- 2 files changed, 227 insertions(+), 115 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index 10684e595a..1c76899669 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -13,33 +13,33 @@ # limitations under the License. """ This module contains the LightningQubit2 class that inherits from the new device interface. - """ +from typing import Optional, Union, Sequence, Callable from dataclasses import replace -from pathlib import Path -from typing import Callable, Optional, Sequence, Union - import numpy as np + import pennylane as qml -from pennylane.devices import DefaultExecutionConfig, Device, ExecutionConfig -from pennylane.devices.modifiers import simulator_tracking, single_tape_support +from pennylane.devices import Device, ExecutionConfig, DefaultExecutionConfig +from pennylane.devices.modifiers import single_tape_support, simulator_tracking from pennylane.devices.preprocess import ( decompose, - no_sampling, validate_device_wires, + decompose, validate_measurements, validate_observables, + no_sampling, ) -from pennylane.tape import QuantumScript, QuantumTape +from pennylane.tape import QuantumTape, QuantumScript from pennylane.transforms.core import TransformProgram from pennylane.typing import Result, ResultBatch -from ._measurements import LightningMeasurements +from ._adjoint_jacobian import LightningAdjointJacobian from ._state_vector import LightningStateVector +from ._measurements import LightningMeasurements try: # pylint: disable=import-error, unused-import - from pennylane_lightning.lightning_qubit_ops import backend_info + import pennylane_lightning.lightning_qubit_ops LQ_CPP_BINARY_AVAILABLE = True except ImportError: @@ -59,22 +59,49 @@ def simulate(circuit: QuantumScript, state: LightningStateVector) -> Result: state (LightningStateVector): handle to Lightning state vector Returns: - tuple(TensorLike): The results of the simulation + Tuple[TensorLike]: The results of the simulation Note that this function can return measurements for non-commuting observables simultaneously. - """ state.reset_state() final_state = state.get_final_state(circuit) return LightningMeasurements(final_state).measure_final_state(circuit) -def jacobian(circuit: QuantumTape): - return np.array(0.0) +def jacobian(circuit: QuantumTape, state: LightningStateVector, batch_obs=False): + """Compute the Jacobian for a single quantum script. + Args: + circuit (QuantumTape): The single circuit to simulate + state (LightningStateVector): handle to Lightning state vector + batch_obs (bool): Determine whether we process observables in parallel when + computing the jacobian. This value is only relevant when the lightning + qubit is built with OpenMP. + + Returns: + TensorLike: The Jacobian of the quantum script + """ + state.reset_state() + final_state = state.get_final_state(circuit) + return LightningAdjointJacobian(final_state, batch_obs=batch_obs).calculate_jacobian(circuit) -def simulate_and_jacobian(circuit: QuantumTape): - return np.array(0.0), np.array(0.0) + +def simulate_and_jacobian(circuit: QuantumTape, state: LightningStateVector, batch_obs=False): + """Simulate a single quantum script and compute its Jacobian. + + Args: + circuit (QuantumTape): The single circuit to simulate + state (LightningStateVector): handle to Lightning state vector + batch_obs (bool): Determine whether we process observables in parallel when + computing the jacobian. This value is only relevant when the lightning + qubit is built with OpenMP. + + Returns: + Tuple[TensorLike]: The results of the simulation and the calculated Jacobian + + Note that this function can return measurements for non-commuting observables simultaneously. + """ + return simulate(circuit, state), jacobian(circuit, state, batch_obs=batch_obs) _operations = frozenset( @@ -158,8 +185,6 @@ def simulate_and_jacobian(circuit: QuantumTape): "QFT", "ECR", "BlockEncode", - "GlobalPhase", - "C(GlobalPhase)", } ) """The set of supported operations.""" @@ -201,13 +226,7 @@ class LightningQubit2(Device): _device_options = ("rng", "c_dtype", "batch_obs", "mcmc", "kernel_name", "num_burnin") - _new_API = True _CPP_BINARY_AVAILABLE = LQ_CPP_BINARY_AVAILABLE - short_name = "lightning.qubit2" - operations = _operations - observables = _observables - _backend_info = backend_info if LQ_CPP_BINARY_AVAILABLE else None - config = Path(__file__).parent / "lightning_qubit.toml" def __init__( # pylint: disable=too-many-arguments self, @@ -260,19 +279,14 @@ def c_dtype(self): return self._c_dtype @property - def C_DTYPE(self): - """State vector complex data type.""" - return self._c_dtype - - @property - def num_wires(self): - """State vector complex data type.""" - return self._statevector.num_wires + def operations(self) -> frozenset[str]: + """The names of the supported operations.""" + return _operations @property - def state(self): - """Returns a copy of the state vector data in a NumPy array.""" - return self._statevector.state + def observables(self) -> frozenset[str]: + """The names of the supported observables.""" + return _observables def _setup_execution_config(self, config): """ @@ -298,19 +312,16 @@ def supports_derivatives( execution_config: Optional[ExecutionConfig] = None, circuit: Optional[qml.tape.QuantumTape] = None, ) -> bool: - if False: - # to be used once adjoint differentiation support is added. - if execution_config is None and circuit is None: - return True - if execution_config.gradient_method not in {"adjoint", "best"}: - return False - if circuit is None: - return True - return ( - all(isinstance(m, qml.measurements.ExpectationMP) for m in circuit.measurements) - and not circuit.shots - ) - return False + if execution_config is None and circuit is None: + return True + if execution_config.gradient_method not in {"adjoint", "best"}: + return False + if circuit is None: + return True + return ( + all(isinstance(m, qml.measurements.ExpectationMP) for m in circuit.measurements) + and not circuit.shots + ) def preprocess(self, execution_config: ExecutionConfig = DefaultExecutionConfig): program = TransformProgram() @@ -340,12 +351,16 @@ def compute_derivatives( circuits: QuantumTape_or_Batch, execution_config: ExecutionConfig = DefaultExecutionConfig, ): - return tuple(jacobian(circuit) for circuit in circuits) + return tuple( + jacobian(circuit, self._statevector, batch_obs=self._batch_obs) for circuit in circuits + ) def execute_and_compute_derivatives( self, circuits: QuantumTape_or_Batch, execution_config: ExecutionConfig = DefaultExecutionConfig, ): - results = tuple(simulate_and_jacobian(c) for c in circuits) + results = tuple( + simulate_and_jacobian(c, self._statevector, batch_obs=self._batch_obs) for c in circuits + ) return tuple(zip(*results)) diff --git a/tests/lightning_qubit2/test_lightning_qubit2.py b/tests/lightning_qubit2/test_lightning_qubit2.py index 30e1a941dd..f35b3ffaaf 100644 --- a/tests/lightning_qubit2/test_lightning_qubit2.py +++ b/tests/lightning_qubit2/test_lightning_qubit2.py @@ -26,10 +26,16 @@ simulate, simulate_and_jacobian, stopping_condition, + decompose, + validate_device_wires, + decompose, + validate_measurements, + validate_observables, + no_sampling, ) from pennylane_lightning.lightning_qubit._state_vector import LightningStateVector from pennylane_lightning.lightning_qubit._measurements import LightningMeasurements -from pennylane.devices import DefaultQubit +from pennylane.devices import DefaultQubit, ExecutionConfig, DefaultExecutionConfig from pennylane.tape import QuantumScript from conftest import LightningDevice # tested device @@ -46,32 +52,7 @@ class TestHelpers: - """Unit tests for the simulate function""" - - # Test simulate - # Test jacobian + xfail tests - # Test simulate_and_jacobian + xfail tests - # Test stopping_condition - # Test accepted_observables - - @pytest.fixture(params=[np.complex64, np.complex128]) - def dev(self, request): - return LightningQubit2(wires=3, c_dtype=request.param) - - @staticmethod - def calculate_reference(tape): - dev = DefaultQubit(max_workers=1) - program, _ = dev.preprocess() - tapes, transf_fn = program([tape]) - results = dev.execute(tapes) - return transf_fn(results) - - @staticmethod - def process_and_execute(dev, tape): - program, _ = dev.preprocess() - tapes, transf_fn = program([tape]) - results = dev.execute(tapes) - return transf_fn(results) + """Unit tests for helper functions""" class DummyOperator(qml.operation.Operation, qml.operation.Observable): """Dummy operator""" @@ -106,44 +87,10 @@ def test_simulate_multi_measurement(self, theta, phi, dev): """Test that simulate returns the correct results with multiple measurements.""" return - @pytest.mark.parametrize("theta", THETA) - def test_jacobian_returns_zero(self, theta): - """Test that jacobian always returns zero.""" - tape = QuantumScript([qml.RX(theta, wires=0)], [qml.expval(qml.Z(0))]) - assert np.allclose(jacobian(tape), 0) - - @pytest.mark.xfail - @pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI))) - def test_jacobian_single_expval(self, theta, phi, dev): - """Test that the jacobian is correct when a tape has a single expectation value""" - return - - @pytest.mark.xfail - @pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI))) - def test_jacobian_multi_expval(self, theta, phi, dev): - """Test that the jacobian is correct when a tape has multiple expectation values""" - return - - @pytest.mark.xfail - @pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI))) - def test_simulate_and_jacobian_single_expval(self, theta, phi, dev): - """Test that the result and jacobian is correct when a tape has a single - expectation value""" - return - - @pytest.mark.xfail - @pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI))) - def test_simulate_and_jacobian_multi_expval(self, theta, phi, dev): - """Test that the result and jacobian is correct when a tape has multiple - expectation values""" - return - class TestInitialization: """Unit tests for LightningQubit2 initialization""" - # Test __init__ errors: invalid num_burnin, kernel name - def test_invalid_num_burnin_error(self): """Test that an error is raised when num_burnin is more than number of shots""" n_shots = 10 @@ -167,13 +114,163 @@ def test_invalid_kernel_name(self): class TestExecution: """Unit tests for executing quantum tapes on LightningQubit2""" - # Test preprocess - # Test execute + @pytest.fixture(params=[np.complex64, np.complex128]) + def dev(self, request): + return LightningQubit2(wires=3, c_dtype=request.param) + + @staticmethod + def calculate_reference(tape): + dev = DefaultQubit(max_workers=1) + program, _ = dev.preprocess() + tapes, transf_fn = program([tape]) + results = dev.execute(tapes) + return transf_fn(results) + + @staticmethod + def process_and_execute(dev, tape): + program, _ = dev.preprocess() + tapes, transf_fn = program([tape]) + results = dev.execute(tapes) + return transf_fn(results) + + _default_device_options = { + "c_dtype": np.complex128, + "batch_obs": False, + "mcmc": False, + "kernel_name": None, + "num_burnin": None, + } + + @pytest.mark.parametrize( + "config, expected_config", + [ + ( + DefaultExecutionConfig, + ExecutionConfig( + grad_on_execution=True, + use_device_gradient=False, + device_options=_default_device_options, + ), + ), + ( + ExecutionConfig(gradient_method="best"), + ExecutionConfig( + gradient_method="adjoint", + grad_on_execution=True, + use_device_gradient=True, + device_options=_default_device_options, + ), + ), + ( + ExecutionConfig( + device_options={ + "c_dtype": np.complex64, + "mcmc": True, + } + ), + ExecutionConfig( + grad_on_execution=True, + use_device_gradient=False, + device_options={ + "c_dtype": np.complex64, + "batch_obs": False, + "mcmc": True, + "kernel_name": None, + "num_burnin": None, + }, + ), + ), + ( + ExecutionConfig( + gradient_method="backprop", use_device_gradient=False, grad_on_execution=False + ), + ExecutionConfig( + gradient_method="backprop", + use_device_gradient=False, + grad_on_execution=False, + device_options=_default_device_options, + ), + ), + ], + ) + def test_preprocess_correct_config_setup(self, config, expected_config): + """Test that the execution config is set up correctly in preprocess""" + dev = LightningQubit2(wires=2) + _, new_config = dev.preprocess(config) + del new_config.device_options["rng"] + + assert new_config == expected_config + + def test_preprocess_correct_transforms(self): + """Test that the transform program returned by preprocess is correct""" + dev = LightningQubit2(wires=2) + + expected_program = qml.transforms.core.TransformProgram() + expected_program.add_transform(validate_measurements, name="LightningQubit2") + expected_program.add_transform(no_sampling) + expected_program.add_transform( + validate_observables, accepted_observables, name="LightningQubit2" + ) + expected_program.add_transform(validate_device_wires, dev.wires, name="LightningQubit2") + expected_program.add_transform(qml.defer_measurements, device=dev) + expected_program.add_transform( + decompose, stopping_condition=stopping_condition, name="LightningQubit2" + ) + expected_program.add_transform(qml.transforms.broadcast_expand) + + actual_program, _ = dev.preprocess(DefaultExecutionConfig) + assert actual_program == expected_program + + # Execution tests class TestDerivatives: """Unit tests for calculating derivatives with LightningQubit2""" + @pytest.fixture(params=[np.complex64, np.complex128]) + def dev(self, request): + return LightningQubit2(wires=3, c_dtype=request.param) + + @staticmethod + def calculate_reference(tape): + dev = DefaultQubit(max_workers=1) + program, _ = dev.preprocess() + tapes, transf_fn = program([tape]) + results = dev.execute(tapes) + return transf_fn(results) + + @staticmethod + def process_and_execute(dev, tape): + program, _ = dev.preprocess() + tapes, transf_fn = program([tape]) + results = dev.execute(tapes) + return transf_fn(results) + # Test supports derivative + xfail tests - # Test compute_derivatives + xfail tests - # Test execute_and_compute_derivatives + xfail tests + + @pytest.mark.parametrize("config, tape, expected", []) + def test_supports_derivatives(self, dev, config, tape, expected): + """Test that supports_derivative returns the correct boolean value.""" + assert dev.supports_derivatives(config, tape) == expected + + @pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI))) + def test_derivative_single_expval(self, theta, phi, dev): + """Test that the jacobian is correct when a tape has a single expectation value""" + return + + @pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI))) + def test_derivative_multi_expval(self, theta, phi, dev): + """Test that the jacobian is correct when a tape has multiple expectation values""" + return + + @pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI))) + def test_execute_and_derivative_single_expval(self, theta, phi, dev): + """Test that the result and jacobian is correct when a tape has a single + expectation value""" + return + + @pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI))) + def test_execute_and_derivative_multi_expval(self, theta, phi, dev): + """Test that the result and jacobian is correct when a tape has multiple + expectation values""" + return From 62d0aa740dff1d56c3b119bc092cf9eef37a34ab Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Fri, 8 Mar 2024 14:54:45 -0500 Subject: [PATCH 190/428] Update pennylane_lightning/lightning_qubit/lightning_qubit2.py Co-authored-by: Amintor Dusko <87949283+AmintorDusko@users.noreply.github.com> --- pennylane_lightning/lightning_qubit/lightning_qubit2.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index 1c76899669..e803d622e8 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -101,7 +101,11 @@ def simulate_and_jacobian(circuit: QuantumTape, state: LightningStateVector, bat Note that this function can return measurements for non-commuting observables simultaneously. """ - return simulate(circuit, state), jacobian(circuit, state, batch_obs=batch_obs) + state.reset_state() + final_state = state.get_final_state(circuit) + measurements = LightningMeasurements(final_state).measure_final_state(circuit) + jacobian = LightningAdjointJacobian(final_state, batch_obs=batch_obs).calculate_jacobian(circuit) + return [measurements, jacobian] _operations = frozenset( From 41772f611dea0fb4ea8aabf1fe09fd30c2d26fff Mon Sep 17 00:00:00 2001 From: Dev version update bot Date: Fri, 8 Mar 2024 19:57:59 +0000 Subject: [PATCH 191/428] Auto update version --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index 25f4c22d24..d519481295 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.36.0-dev4" +__version__ = "0.36.0-dev5" From 4f6f609984bc34bfdee0e904ca0b1de8862ec6dc Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Fri, 8 Mar 2024 15:05:21 -0500 Subject: [PATCH 192/428] Small update to simulate_and_jacobian --- pennylane_lightning/lightning_qubit/lightning_qubit2.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index e803d622e8..b87209fa85 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -101,11 +101,9 @@ def simulate_and_jacobian(circuit: QuantumTape, state: LightningStateVector, bat Note that this function can return measurements for non-commuting observables simultaneously. """ - state.reset_state() - final_state = state.get_final_state(circuit) - measurements = LightningMeasurements(final_state).measure_final_state(circuit) - jacobian = LightningAdjointJacobian(final_state, batch_obs=batch_obs).calculate_jacobian(circuit) - return [measurements, jacobian] + res = simulate(circuit, state) + jacobian = LightningAdjointJacobian(state, batch_obs=batch_obs).calculate_jacobian(circuit) + return [res, jacobian] _operations = frozenset( From 7c4db258f353d85c930b778075b24e60731c20d7 Mon Sep 17 00:00:00 2001 From: Dev version update bot Date: Mon, 18 Mar 2024 13:12:10 +0000 Subject: [PATCH 193/428] Auto update version --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index f95ce9d020..5bc57662cf 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.36.0-dev10" +__version__ = "0.36.0-dev11" From 02394dd903ffd4211a82bdeac7013cf9f48989a5 Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Mon, 18 Mar 2024 13:12:59 +0000 Subject: [PATCH 194/428] Rerun isort. --- pennylane_lightning/lightning_gpu/lightning_gpu.py | 14 +++++++------- .../lightning_qubit/lightning_qubit.py | 8 ++++---- .../lightning_qubit/lightning_qubit2.py | 11 +++++------ tests/test_apply.py | 1 + 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/pennylane_lightning/lightning_gpu/lightning_gpu.py b/pennylane_lightning/lightning_gpu/lightning_gpu.py index 185115985b..cbc4a48340 100644 --- a/pennylane_lightning/lightning_gpu/lightning_gpu.py +++ b/pennylane_lightning/lightning_gpu/lightning_gpu.py @@ -91,13 +91,6 @@ from pennylane.ops.op_math import Adjoint from pennylane.wires import Wires - # pylint: disable=import-error, no-name-in-module, ungrouped-imports - from pennylane_lightning.core._serialize import ( - QuantumScriptSerializer, - global_phase_diagonal, - ) - from pennylane_lightning.core._version import __version__ - # pylint: disable=no-name-in-module, ungrouped-imports from pennylane_lightning.lightning_gpu_ops.algorithms import ( AdjointJacobianC64, @@ -106,6 +99,13 @@ create_ops_listC128, ) + # pylint: disable=import-error, no-name-in-module, ungrouped-imports + from pennylane_lightning.core._serialize import ( + QuantumScriptSerializer, + global_phase_diagonal, + ) + from pennylane_lightning.core._version import __version__ + if MPI_SUPPORT: from pennylane_lightning.lightning_gpu_ops.algorithmsMPI import ( AdjointJacobianMPIC64, diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit.py b/pennylane_lightning/lightning_qubit/lightning_qubit.py index ffdb5e33a4..de7c863a85 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit.py @@ -66,6 +66,10 @@ from pennylane.ops.op_math import Adjoint from pennylane.wires import Wires + # pylint: disable=import-error, no-name-in-module, ungrouped-imports + from pennylane_lightning.core._serialize import QuantumScriptSerializer + from pennylane_lightning.core._version import __version__ + # pylint: disable=no-name-in-module, ungrouped-imports from pennylane_lightning.lightning_qubit_ops.algorithms import ( AdjointJacobianC64, @@ -76,10 +80,6 @@ create_ops_listC128, ) - # pylint: disable=import-error, no-name-in-module, ungrouped-imports - from pennylane_lightning.core._serialize import QuantumScriptSerializer - from pennylane_lightning.core._version import __version__ - def _state_dtype(dtype): if dtype not in [np.complex128, np.complex64]: # pragma: no cover raise ValueError(f"Data type is not supported for state-vector computation: {dtype}") diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index 2c53a0220a..474939970a 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -15,8 +15,8 @@ This module contains the LightningQubit2 class that inherits from the new device interface. """ from dataclasses import replace -from typing import Callable, Optional, Sequence, Union from pathlib import Path +from typing import Callable, Optional, Sequence, Union import numpy as np import pennylane as qml @@ -230,7 +230,6 @@ def accepted_observables(obs: qml.operation.Operator) -> bool: return obs.name in _observables - def adjoint_measurements(mp: qml.measurements.MeasurementProcess) -> bool: """Specifies whether or not an observable is compatible with adjoint differentiation on DefaultQubit.""" return isinstance(mp, qml.measurements.ExpectationMP) @@ -458,10 +457,10 @@ def execute( TensorLike, tuple[TensorLike], tuple[tuple[TensorLike]]: A numeric result of the computation. """ mcmc = { - "mcmc": self._mcmc, - "kernel_name": self._kernel_name, - "num_burnin": self._num_burnin, - } + "mcmc": self._mcmc, + "kernel_name": self._kernel_name, + "num_burnin": self._num_burnin, + } results = [] for circuit in circuits: circuit = circuit.map_to_standard_wires() diff --git a/tests/test_apply.py b/tests/test_apply.py index b2e81ffd20..7240e1a7aa 100644 --- a/tests/test_apply.py +++ b/tests/test_apply.py @@ -29,6 +29,7 @@ if ld._new_API and not ld._CPP_BINARY_AVAILABLE: pytest.skip("No binary module found. Skipping.", allow_module_level=True) + @pytest.mark.skipif(ld._new_API, reason="Old API required") class TestApply: """Tests that operations of certain operations are applied correctly or From 3f24255b11207b635cc17241b7fdb77a4114faee Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Mon, 18 Mar 2024 14:25:56 +0000 Subject: [PATCH 195/428] Uncomment integration tests. --- .../lightning_qubit/lightning_qubit2.py | 7 +------ requirements-dev.txt | 2 +- tests/test_adjoint_jacobian.py | 3 --- tests/test_apply.py | 10 ---------- tests/test_expval.py | 6 ------ tests/test_measurements.py | 1 - tests/test_var.py | 2 -- 7 files changed, 2 insertions(+), 29 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index 474939970a..c0c4d902f5 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -375,20 +375,15 @@ def __init__( # pylint: disable=too-many-arguments def c_dtype(self): """State vector complex data type.""" return self._c_dtype - C_DTYPE = c_dtype dtype = c_dtype - @property - def num_wires(self): - """Number of wires in the state vector.""" - return self._statevector.num_wires - @property def state(self): """Returns a copy of the state vector data in a NumPy array.""" return self._statevector.state + def _setup_execution_config(self, config): """ Update the execution config with choices for how the device should be used and the device options. diff --git a/requirements-dev.txt b/requirements-dev.txt index ba5b081ac3..a4ceae1c8c 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,5 +1,5 @@ pip~=22.0 -git+https://github.com/PennyLaneAI/pennylane.git@bugfix/lightning_rotations +git+https://github.com/PennyLaneAI/pennylane.git@master ninja flaky pybind11 diff --git a/tests/test_adjoint_jacobian.py b/tests/test_adjoint_jacobian.py index 8ec5b3c495..4fc5058c4c 100644 --- a/tests/test_adjoint_jacobian.py +++ b/tests/test_adjoint_jacobian.py @@ -26,9 +26,6 @@ from pennylane import qchem, qnode from scipy.stats import unitary_group -if ld._new_API: - pytest.skip("Old API required", allow_module_level=True) - I, X, Y, Z = ( np.eye(2), qml.X.compute_matrix(), diff --git a/tests/test_apply.py b/tests/test_apply.py index 7240e1a7aa..39f9e886b5 100644 --- a/tests/test_apply.py +++ b/tests/test_apply.py @@ -533,7 +533,6 @@ def test_apply_state_vector_lightning_handle(self, qubit_device, tol): class TestExpval: """Tests that expectation values are properly calculated or that the proper errors are raised.""" - @pytest.mark.skipif(ld._new_API, reason="Old API required") @pytest.mark.parametrize( "operation,input,expected_output", [ @@ -572,7 +571,6 @@ def test_expval_single_wire_no_parameters( assert np.isclose(res, expected_output, atol=tol, rtol=0) - @pytest.mark.xfail(ld._new_API, reason="Old API required") def test_expval_estimate(self): """Test that the expectation value is not analytically calculated""" dev = qml.device(device_name, wires=1, shots=3) @@ -591,7 +589,6 @@ def circuit(): class TestVar: """Tests that variances are properly calculated.""" - @pytest.mark.skipif(ld._new_API, reason="Old API required") @pytest.mark.parametrize( "operation,input,expected_output", [ @@ -630,7 +627,6 @@ def test_var_single_wire_no_parameters( assert np.isclose(res, expected_output, atol=tol, rtol=0) - @pytest.mark.xfail(ld._new_API, reason="Old API required") def test_var_estimate(self): """Test that the variance is not analytically calculated""" @@ -647,7 +643,6 @@ def circuit(): assert var != 1.0 -@pytest.mark.skipif(ld._new_API, reason="Old API required") class TestSample: """Tests that samples are properly calculated.""" @@ -812,7 +807,6 @@ def circuit(x): assert np.isclose(circuit(p), 1, atol=tol, rtol=0) - @pytest.mark.xfail(ld._new_API, reason="Old API required") def test_nonzero_shots(self, tol_stochastic): """Test that the default qubit plugin provides correct result for high shot number""" @@ -1163,7 +1157,6 @@ def circuit(): assert np.isclose(circuit(), expected_output, atol=tol, rtol=0) - @pytest.mark.xfail(ld._new_API, reason="Old API required") def test_multi_samples_return_correlated_results(self, qubit_device): """Tests if the samples returned by the sample function have the correct dimensions @@ -1185,7 +1178,6 @@ def circuit(): assert np.array_equal(outcomes[0], outcomes[1]) - @pytest.mark.xfail(ld._new_API, reason="Old API required") @pytest.mark.parametrize("num_wires", [3, 4, 5, 6, 7, 8]) def test_multi_samples_return_correlated_results_more_wires_than_size_of_observable( self, num_wires @@ -1211,7 +1203,6 @@ def circuit(): assert np.array_equal(outcomes[0], outcomes[1]) - @pytest.mark.xfail(ld._new_API, reason="Old API required") def test_snapshot_is_ignored_without_shot(self): """Tests if the Snapshot operator is ignored correctly""" dev = qml.device(device_name, wires=4) @@ -1228,7 +1219,6 @@ def circuit(): assert np.allclose(outcomes, [0.0]) - @pytest.mark.xfail(ld._new_API, reason="Old API required") def test_snapshot_is_ignored_with_shots(self): """Tests if the Snapshot operator is ignored correctly""" dev = qml.device(device_name, wires=4, shots=1000) diff --git a/tests/test_expval.py b/tests/test_expval.py index 5a23f4bac4..62ef646229 100644 --- a/tests/test_expval.py +++ b/tests/test_expval.py @@ -30,7 +30,6 @@ class TestExpval: """Test expectation values""" - @pytest.mark.skipif(ld._new_API, reason="Old API required") def test_identity_expectation(self, theta, phi, qubit_device, tol): """Test that identity expectation value (i.e. the trace) is 1""" dev = qubit_device(wires=3) @@ -49,7 +48,6 @@ def test_identity_expectation(self, theta, phi, qubit_device, tol): res = np.array([dev.expval(O1), dev.expval(O2)]) assert np.allclose(res, np.array([1, 1]), tol) - @pytest.mark.skipif(ld._new_API, reason="Old API required") def test_pauliz_expectation(self, theta, phi, qubit_device, tol): """Test that PauliZ expectation value is correct""" dev = qubit_device(wires=3) @@ -69,7 +67,6 @@ def test_pauliz_expectation(self, theta, phi, qubit_device, tol): res = np.array([dev.expval(O1), dev.expval(O2)]) assert np.allclose(res, np.array([np.cos(theta), np.cos(theta) * np.cos(phi)]), tol) - @pytest.mark.skipif(ld._new_API, reason="Old API required") def test_paulix_expectation(self, theta, phi, qubit_device, tol): """Test that PauliX expectation value is correct""" dev = qubit_device(wires=3) @@ -92,7 +89,6 @@ def test_paulix_expectation(self, theta, phi, qubit_device, tol): res, np.array([np.sin(theta) * np.sin(phi), np.sin(phi)], dtype=dev.C_DTYPE), tol * 10 ) - @pytest.mark.skipif(ld._new_API, reason="Old API required") def test_pauliy_expectation(self, theta, phi, qubit_device, tol): """Test that PauliY expectation value is correct""" dev = qubit_device(wires=3) @@ -113,7 +109,6 @@ def test_pauliy_expectation(self, theta, phi, qubit_device, tol): res = np.array([dev.expval(O1), dev.expval(O2)]) assert np.allclose(res, np.array([0, -np.cos(theta) * np.sin(phi)]), tol) - @pytest.mark.skipif(ld._new_API, reason="Old API required") def test_hadamard_expectation(self, theta, phi, qubit_device, tol): """Test that Hadamard expectation value is correct""" dev = qubit_device(wires=3) @@ -279,7 +274,6 @@ def circuit(x, y): assert qml.math.allclose(g, expected) -@pytest.mark.skipif(ld._new_API, reason="Old API required") @pytest.mark.parametrize("theta,phi,varphi", list(zip(THETA, PHI, VARPHI))) class TestTensorExpval: """Test tensor expectation values""" diff --git a/tests/test_measurements.py b/tests/test_measurements.py index 136fc1009e..a1bfa173c4 100644 --- a/tests/test_measurements.py +++ b/tests/test_measurements.py @@ -592,7 +592,6 @@ def circuit2(): assert np.allclose(circuit1(), circuit2(), atol=tol) -@pytest.mark.skipif(ld._new_API, reason="Old API required") class TestSample: """Tests that samples are properly calculated.""" diff --git a/tests/test_var.py b/tests/test_var.py index 92fdffd52e..aaa86f1ce7 100644 --- a/tests/test_var.py +++ b/tests/test_var.py @@ -30,7 +30,6 @@ class TestVar: """Tests for the variance""" - @pytest.mark.skipif(ld._new_API, reason="Old API required") def test_var(self, theta, phi, qubit_device, tol): """Tests for variance calculation""" dev = qubit_device(wires=3) @@ -79,7 +78,6 @@ def circuit(): assert np.allclose(circ(), circ_def(), tol) -@pytest.mark.skipif(ld._new_API, reason="Old API required") @pytest.mark.parametrize("theta, phi, varphi", list(zip(THETA, PHI, VARPHI))) class TestTensorVar: """Tests for variance of tensor observables""" From 62411b6a5bb3514130412f2a38a3ceb81be454b7 Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Mon, 18 Mar 2024 14:26:23 +0000 Subject: [PATCH 196/428] Reformat --- pennylane_lightning/lightning_qubit/lightning_qubit2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index c0c4d902f5..ce433ae77d 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -375,6 +375,7 @@ def __init__( # pylint: disable=too-many-arguments def c_dtype(self): """State vector complex data type.""" return self._c_dtype + C_DTYPE = c_dtype dtype = c_dtype @@ -383,7 +384,6 @@ def state(self): """Returns a copy of the state vector data in a NumPy array.""" return self._statevector.state - def _setup_execution_config(self, config): """ Update the execution config with choices for how the device should be used and the device options. From 791600ec506510bd35c8d22404371d21402f0257 Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Mon, 18 Mar 2024 14:33:05 +0000 Subject: [PATCH 197/428] Delete symlink --- pennylane_lightning/lightning_qubit2 | 1 - 1 file changed, 1 deletion(-) delete mode 120000 pennylane_lightning/lightning_qubit2 diff --git a/pennylane_lightning/lightning_qubit2 b/pennylane_lightning/lightning_qubit2 deleted file mode 120000 index 0bcda1f466..0000000000 --- a/pennylane_lightning/lightning_qubit2 +++ /dev/null @@ -1 +0,0 @@ -lightning_qubit \ No newline at end of file From a39f7a60025fe734d89a8506a07392941a57c61c Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Mon, 18 Mar 2024 14:37:28 +0000 Subject: [PATCH 198/428] Fix pylint. --- .../lightning_qubit/_measurements.py | 21 +++++++------------ .../lightning_qubit/lightning_qubit2.py | 4 +++- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/_measurements.py b/pennylane_lightning/lightning_qubit/_measurements.py index 992f2f3461..1ec878da25 100644 --- a/pennylane_lightning/lightning_qubit/_measurements.py +++ b/pennylane_lightning/lightning_qubit/_measurements.py @@ -24,14 +24,11 @@ except ImportError: pass -from typing import Callable, List, Sequence, Union +from typing import Callable, List, Union import numpy as np import pennylane as qml -from pennylane.devices.qubit.sampling import ( - _group_measurements, - _measure_with_samples_diagonalizing_gates, -) +from pennylane.devices.qubit.sampling import _group_measurements from pennylane.measurements import ( ClassicalShadowMP, CountsMP, @@ -316,19 +313,17 @@ def measure_with_samples( all_res = [] for group in groups: if isinstance(group[0], ExpectationMP) and isinstance(group[0].obs, Hamiltonian): - raise TypeError(f"ExpectationMP(Hamiltonian) cannot be computed with samples.") + raise TypeError("ExpectationMP(Hamiltonian) cannot be computed with samples.") # measure_fn = _measure_hamiltonian_with_samples - elif isinstance(group[0], ExpectationMP) and isinstance(group[0].obs, Sum): - raise TypeError(f"ExpectationMP(Sum) cannot be computed with samples.") + if isinstance(group[0], ExpectationMP) and isinstance(group[0].obs, Sum): + raise TypeError("ExpectationMP(Sum) cannot be computed with samples.") # measure_fn = _measure_sum_with_samples - elif isinstance(group[0], (ClassicalShadowMP, ShadowExpvalMP)): + if isinstance(group[0], (ClassicalShadowMP, ShadowExpvalMP)): raise TypeError( - f"ExpectationMP(ClassicalShadowMP, ShadowExpvalMP) cannot be computed with samples." + "ExpectationMP(ClassicalShadowMP, ShadowExpvalMP) cannot be computed with samples." ) # measure_fn = _measure_classical_shadow - else: - # measure with the usual method (rotate into the measurement basis) - all_res.extend(self._measure_with_samples_diagonalizing_gates(group, shots)) + all_res.extend(self._measure_with_samples_diagonalizing_gates(group, shots)) flat_indices = [_i for i in indices for _i in i] diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index ce433ae77d..8b730a6da8 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -53,7 +53,7 @@ PostprocessingFn = Callable[[ResultBatch], Result_or_ResultBatch] -def simulate(circuit: QuantumScript, state: LightningStateVector, mcmc: dict = {}) -> Result: +def simulate(circuit: QuantumScript, state: LightningStateVector, mcmc: dict = None) -> Result: """Simulate a single quantum script. Args: @@ -65,6 +65,8 @@ def simulate(circuit: QuantumScript, state: LightningStateVector, mcmc: dict = { Note that this function can return measurements for non-commuting observables simultaneously. """ + if mcmc is None: + mcmc = {} state.reset_state() final_state = state.get_final_state(circuit) return LightningMeasurements(final_state, **mcmc).measure_final_state(circuit) From 12ba7574523fb1a9cac8d920c2dbf71ca5ab53eb Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Mon, 18 Mar 2024 15:29:39 +0000 Subject: [PATCH 199/428] Run linux tests in parallel (when possible). --- .github/workflows/tests_linux.yml | 28 +++++++++++-------- .../lightning_qubit/lightning_qubit2.py | 5 ---- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/.github/workflows/tests_linux.yml b/.github/workflows/tests_linux.yml index 4a0c85bd9e..ee9c3f8adc 100644 --- a/.github/workflows/tests_linux.yml +++ b/.github/workflows/tests_linux.yml @@ -184,9 +184,11 @@ jobs: run: | cd main/ DEVICENAME=`echo ${{ matrix.pl_backend }} | sed "s/_/./g"` - PL_DEVICE=${DEVICENAME} python -m pytest tests/ $COVERAGE_FLAGS - PL_DEVICE=lightning_qubit2 python -m pytest tests/ $COVERAGE_FLAGS --cov-append - pl-device-test --device ${DEVICENAME} --skip-ops --shots=20000 $COVERAGE_FLAGS --cov-append + PL_DEVICE=${DEVICENAME} python -m pytest -n auto tests/ -k "not unitary_correct" $COVERAGE_FLAGS + PL_DEVICE=${DEVICENAME} python -m pytest tests/ -k "unitary_correct" $COVERAGE_FLAGS --cov-append + PL_DEVICE=lightning_qubit2 python -m pytest -n auto tests/ -k "not unitary_correct" $COVERAGE_FLAGS --cov-append + PL_DEVICE=lightning_qubit2 python -m pytest tests/ -k "unitary_correct" $COVERAGE_FLAGS --cov-append --cov-append + pl-device-test --device ${DEVICENAME} --skip-ops --shots=40000 $COVERAGE_FLAGS --cov-append pl-device-test --device ${DEVICENAME} --shots=None --skip-ops $COVERAGE_FLAGS --cov-append mv .coverage .coverage-${{ github.job }}-${{ matrix.pl_backend }} @@ -393,8 +395,9 @@ jobs: run: | cd main/ DEVICENAME=`echo ${{ matrix.pl_backend }} | sed "s/_/./g"` - PL_DEVICE=${DEVICENAME} python -m pytest tests/ $COVERAGE_FLAGS - pl-device-test --device ${DEVICENAME} --skip-ops --shots=20000 $COVERAGE_FLAGS --cov-append + PL_DEVICE=${DEVICENAME} python -m pytest -n auto tests/ -k "not unitary_correct" $COVERAGE_FLAGS + PL_DEVICE=${DEVICENAME} python -m pytest tests/ -k "unitary_correct" $COVERAGE_FLAGS --cov-append + pl-device-test --device ${DEVICENAME} --skip-ops --shots=40000 $COVERAGE_FLAGS --cov-append pl-device-test --device ${DEVICENAME} --shots=None --skip-ops $COVERAGE_FLAGS --cov-append mv .coverage .coverage-${{ github.job }}-${{ matrix.pl_backend }} @@ -591,8 +594,9 @@ jobs: run: | cd main/ DEVICENAME=`echo ${{ matrix.pl_backend }} | sed "s/_/./g"` - PL_DEVICE=${DEVICENAME} python -m pytest tests/ $COVERAGE_FLAGS - pl-device-test --device ${DEVICENAME} --skip-ops --shots=20000 $COVERAGE_FLAGS --cov-append + OMP_NUM_THREADS=2 PL_DEVICE=${DEVICENAME} python -m pytest -n auto tests/ -k "not unitary_correct" $COVERAGE_FLAGS + PL_DEVICE=${DEVICENAME} python -m pytest tests/ -k "unitary_correct" $COVERAGE_FLAGS --cov-append + pl-device-test --device ${DEVICENAME} --skip-ops --shots=40000 $COVERAGE_FLAGS --cov-append pl-device-test --device ${DEVICENAME} --shots=None --skip-ops $COVERAGE_FLAGS --cov-append mv .coverage .coverage-${{ github.job }}-${{ matrix.pl_backend }} @@ -610,11 +614,13 @@ jobs: if: ${{ matrix.pl_backend == 'all' }} run: | cd main/ - PL_DEVICE=lightning.qubit python -m pytest tests/ $COVERAGE_FLAGS - pl-device-test --device lightning.qubit --skip-ops --shots=20000 $COVERAGE_FLAGS --cov-append + OMP_NUM_THREADS=2 PL_DEVICE=lightning.qubit python -m pytest -n auto tests/ -k "not unitary_correct" $COVERAGE_FLAGS + PL_DEVICE=lightning.qubit python -m pytest tests/ -k "unitary_correct" $COVERAGE_FLAGS --cov-append + pl-device-test --device lightning.qubit --skip-ops --shots=40000 $COVERAGE_FLAGS --cov-append pl-device-test --device lightning.qubit --shots=None --skip-ops $COVERAGE_FLAGS --cov-append - PL_DEVICE=lightning.kokkos python -m pytest tests/ $COVERAGE_FLAGS - pl-device-test --device lightning.kokkos --skip-ops --shots=20000 $COVERAGE_FLAGS --cov-append + OMP_NUM_THREADS=2 PL_DEVICE=lightning.kokkos python -m pytest -n auto tests/ -k "not unitary_correct" $COVERAGE_FLAGS --cov-append + PL_DEVICE=lightning.kokkos python -m pytest tests/ -k "unitary_correct" $COVERAGE_FLAGS --cov-append + pl-device-test --device lightning.kokkos --skip-ops --shots=40000 $COVERAGE_FLAGS --cov-append pl-device-test --device lightning.kokkos --shots=None --skip-ops $COVERAGE_FLAGS --cov-append mv .coverage .coverage-${{ github.job }}-${{ matrix.pl_backend }} diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index 8b730a6da8..a8d2a38fd2 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -381,11 +381,6 @@ def c_dtype(self): C_DTYPE = c_dtype dtype = c_dtype - @property - def state(self): - """Returns a copy of the state vector data in a NumPy array.""" - return self._statevector.state - def _setup_execution_config(self, config): """ Update the execution config with choices for how the device should be used and the device options. From 147e75ef83cbb76cd042dd98b5c502038437aea8 Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Mon, 18 Mar 2024 15:44:00 +0000 Subject: [PATCH 200/428] Run double obs tests with shots. --- .../test_measurements_class.py | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/tests/lightning_qubit/test_measurements_class.py b/tests/lightning_qubit/test_measurements_class.py index 428825cced..eae8f1fa44 100644 --- a/tests/lightning_qubit/test_measurements_class.py +++ b/tests/lightning_qubit/test_measurements_class.py @@ -480,6 +480,7 @@ def test_single_return_value(self, measurement, observable, lightning_sv, tol): assert np.allclose(result, expected, max(tol, 1.0e-5)) @flaky(max_runs=5) + @pytest.mark.parametrize("shots", [None, 100000]) @pytest.mark.parametrize("measurement", [qml.expval, qml.probs, qml.var]) @pytest.mark.parametrize( "obs0_", @@ -515,21 +516,24 @@ def test_single_return_value(self, measurement, observable, lightning_sv, tol): qml.SparseHamiltonian(get_sparse_hermitian_matrix(2**4), wires=range(4)), ), ) - def test_double_return_value(self, measurement, obs0_, obs1_, lightning_sv, tol): - if measurement is qml.probs and isinstance( - obs0_, - (qml.ops.Sum, qml.ops.SProd, qml.ops.Prod, qml.Hamiltonian, qml.SparseHamiltonian), + def test_double_return_value(self, shots, measurement, obs0_, obs1_, lightning_sv, tol): + skip_list = ( + qml.ops.Sum, + qml.ops.SProd, + qml.ops.Prod, + qml.Hamiltonian, + qml.SparseHamiltonian, + ) + if measurement is qml.probs and ( + isinstance(obs0_, skip_list) or isinstance(obs1_, skip_list) ): pytest.skip( f"Observable of type {type(obs0_).__name__} is not supported for rotating probabilities." ) - if measurement is qml.probs and isinstance( - obs1_, - (qml.ops.Sum, qml.ops.SProd, qml.ops.Prod, qml.Hamiltonian, qml.SparseHamiltonian), - ): + if shots is not None and (isinstance(obs0_, skip_list) or isinstance(obs1_, skip_list)): pytest.skip( - f"Observable of type {type(obs1_).__name__} is not supported for rotating probabilities." + f"Observable of type {type(obs0_).__name__} is not supported when finite shots." ) n_qubits = 4 @@ -539,7 +543,7 @@ def test_double_return_value(self, measurement, obs0_, obs1_, lightning_sv, tol) ops = [qml.Hadamard(i) for i in range(n_qubits)] ops += [qml.StronglyEntanglingLayers(weights, wires=range(n_qubits))] measurements = [measurement(op=obs0_), measurement(op=obs1_)] - tape = qml.tape.QuantumScript(ops, measurements) + tape = qml.tape.QuantumScript(ops, measurements, shots=shots) expected = self.calculate_reference(tape, lightning_sv) if len(expected) == 1: @@ -552,8 +556,9 @@ def test_double_return_value(self, measurement, obs0_, obs1_, lightning_sv, tol) assert isinstance(result, Sequence) assert len(result) == len(expected) # a few tests may fail in single precision, and hence we increase the tolerance + dtol = tol if shots is None else max(tol, 1.0e-2) for r, e in zip(result, expected): - assert np.allclose(r, e, max(tol, 1.0e-5)) + assert np.allclose(r, e, atol=dtol, rtol=dtol) @pytest.mark.parametrize( "cases", From d4416f869024b221bf0aacf512371b92a9e19763 Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Mon, 18 Mar 2024 16:07:00 +0000 Subject: [PATCH 201/428] Revert linux tests --- .github/workflows/tests_linux.yml | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/.github/workflows/tests_linux.yml b/.github/workflows/tests_linux.yml index ee9c3f8adc..4a0c85bd9e 100644 --- a/.github/workflows/tests_linux.yml +++ b/.github/workflows/tests_linux.yml @@ -184,11 +184,9 @@ jobs: run: | cd main/ DEVICENAME=`echo ${{ matrix.pl_backend }} | sed "s/_/./g"` - PL_DEVICE=${DEVICENAME} python -m pytest -n auto tests/ -k "not unitary_correct" $COVERAGE_FLAGS - PL_DEVICE=${DEVICENAME} python -m pytest tests/ -k "unitary_correct" $COVERAGE_FLAGS --cov-append - PL_DEVICE=lightning_qubit2 python -m pytest -n auto tests/ -k "not unitary_correct" $COVERAGE_FLAGS --cov-append - PL_DEVICE=lightning_qubit2 python -m pytest tests/ -k "unitary_correct" $COVERAGE_FLAGS --cov-append --cov-append - pl-device-test --device ${DEVICENAME} --skip-ops --shots=40000 $COVERAGE_FLAGS --cov-append + PL_DEVICE=${DEVICENAME} python -m pytest tests/ $COVERAGE_FLAGS + PL_DEVICE=lightning_qubit2 python -m pytest tests/ $COVERAGE_FLAGS --cov-append + pl-device-test --device ${DEVICENAME} --skip-ops --shots=20000 $COVERAGE_FLAGS --cov-append pl-device-test --device ${DEVICENAME} --shots=None --skip-ops $COVERAGE_FLAGS --cov-append mv .coverage .coverage-${{ github.job }}-${{ matrix.pl_backend }} @@ -395,9 +393,8 @@ jobs: run: | cd main/ DEVICENAME=`echo ${{ matrix.pl_backend }} | sed "s/_/./g"` - PL_DEVICE=${DEVICENAME} python -m pytest -n auto tests/ -k "not unitary_correct" $COVERAGE_FLAGS - PL_DEVICE=${DEVICENAME} python -m pytest tests/ -k "unitary_correct" $COVERAGE_FLAGS --cov-append - pl-device-test --device ${DEVICENAME} --skip-ops --shots=40000 $COVERAGE_FLAGS --cov-append + PL_DEVICE=${DEVICENAME} python -m pytest tests/ $COVERAGE_FLAGS + pl-device-test --device ${DEVICENAME} --skip-ops --shots=20000 $COVERAGE_FLAGS --cov-append pl-device-test --device ${DEVICENAME} --shots=None --skip-ops $COVERAGE_FLAGS --cov-append mv .coverage .coverage-${{ github.job }}-${{ matrix.pl_backend }} @@ -594,9 +591,8 @@ jobs: run: | cd main/ DEVICENAME=`echo ${{ matrix.pl_backend }} | sed "s/_/./g"` - OMP_NUM_THREADS=2 PL_DEVICE=${DEVICENAME} python -m pytest -n auto tests/ -k "not unitary_correct" $COVERAGE_FLAGS - PL_DEVICE=${DEVICENAME} python -m pytest tests/ -k "unitary_correct" $COVERAGE_FLAGS --cov-append - pl-device-test --device ${DEVICENAME} --skip-ops --shots=40000 $COVERAGE_FLAGS --cov-append + PL_DEVICE=${DEVICENAME} python -m pytest tests/ $COVERAGE_FLAGS + pl-device-test --device ${DEVICENAME} --skip-ops --shots=20000 $COVERAGE_FLAGS --cov-append pl-device-test --device ${DEVICENAME} --shots=None --skip-ops $COVERAGE_FLAGS --cov-append mv .coverage .coverage-${{ github.job }}-${{ matrix.pl_backend }} @@ -614,13 +610,11 @@ jobs: if: ${{ matrix.pl_backend == 'all' }} run: | cd main/ - OMP_NUM_THREADS=2 PL_DEVICE=lightning.qubit python -m pytest -n auto tests/ -k "not unitary_correct" $COVERAGE_FLAGS - PL_DEVICE=lightning.qubit python -m pytest tests/ -k "unitary_correct" $COVERAGE_FLAGS --cov-append - pl-device-test --device lightning.qubit --skip-ops --shots=40000 $COVERAGE_FLAGS --cov-append + PL_DEVICE=lightning.qubit python -m pytest tests/ $COVERAGE_FLAGS + pl-device-test --device lightning.qubit --skip-ops --shots=20000 $COVERAGE_FLAGS --cov-append pl-device-test --device lightning.qubit --shots=None --skip-ops $COVERAGE_FLAGS --cov-append - OMP_NUM_THREADS=2 PL_DEVICE=lightning.kokkos python -m pytest -n auto tests/ -k "not unitary_correct" $COVERAGE_FLAGS --cov-append - PL_DEVICE=lightning.kokkos python -m pytest tests/ -k "unitary_correct" $COVERAGE_FLAGS --cov-append - pl-device-test --device lightning.kokkos --skip-ops --shots=40000 $COVERAGE_FLAGS --cov-append + PL_DEVICE=lightning.kokkos python -m pytest tests/ $COVERAGE_FLAGS + pl-device-test --device lightning.kokkos --skip-ops --shots=20000 $COVERAGE_FLAGS --cov-append pl-device-test --device lightning.kokkos --shots=None --skip-ops $COVERAGE_FLAGS --cov-append mv .coverage .coverage-${{ github.job }}-${{ matrix.pl_backend }} From 349400bd814119310c6550dcd8761f0fe56bc441 Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Mon, 18 Mar 2024 17:40:21 +0000 Subject: [PATCH 202/428] Fix bg in diag_gates. --- .../lightning_qubit/_measurements.py | 32 ++++++++----------- .../lightning_qubit/_state_vector.py | 13 +++++--- .../test_measurements_class.py | 32 ++++++++++++------- 3 files changed, 44 insertions(+), 33 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/_measurements.py b/pennylane_lightning/lightning_qubit/_measurements.py index 1ec878da25..9a01188a36 100644 --- a/pennylane_lightning/lightning_qubit/_measurements.py +++ b/pennylane_lightning/lightning_qubit/_measurements.py @@ -41,7 +41,7 @@ StateMeasurement, VarianceMP, ) -from pennylane.ops import Hamiltonian, Sum +from pennylane.ops import Hamiltonian, SparseHamiltonian, Sum from pennylane.tape import QuantumScript from pennylane.typing import Result, TensorLike from pennylane.wires import Wires @@ -68,9 +68,8 @@ def __init__( num_burnin: int = 100, ) -> None: self._qubit_state = qubit_state - self._state = qubit_state.state_vector self._dtype = qubit_state.dtype - self._measurement_lightning = self._measurement_dtype()(self.state) + self._measurement_lightning = self._measurement_dtype()(qubit_state.state_vector) self._mcmc = mcmc self._kernel_name = kernel_name self._num_burnin = num_burnin @@ -80,11 +79,6 @@ def qubit_state(self): """Returns a handle to the LightningStateVector class.""" return self._qubit_state - @property - def state(self): - """Returns a handle to the Lightning internal data class.""" - return self._state - @property def dtype(self): """Returns the simulation data type.""" @@ -108,14 +102,11 @@ def state_diagonalizing_gates(self, measurementprocess: StateMeasurement) -> Ten TensorLike: the result of the measurement """ diagonalizing_gates = measurementprocess.diagonalizing_gates() - self._qubit_state.apply_operations(measurementprocess.diagonalizing_gates()) + self._qubit_state.apply_operations(diagonalizing_gates) state_array = self._qubit_state.state wires = Wires(range(self._qubit_state.num_wires)) - result = measurementprocess.process_state(state_array, wires) - self._qubit_state.apply_operations([qml.adjoint(g) for g in reversed(diagonalizing_gates)]) - return result # pylint: disable=protected-access @@ -312,17 +303,20 @@ def measure_with_samples( all_res = [] for group in groups: - if isinstance(group[0], ExpectationMP) and isinstance(group[0].obs, Hamiltonian): + if isinstance(group[0], (ExpectationMP, VarianceMP)) and isinstance( + group[0].obs, SparseHamiltonian + ): + raise TypeError("ExpectationMP(SparseHamiltonian) cannot be computed with samples.") + if isinstance(group[0], (ExpectationMP, VarianceMP)) and isinstance( + group[0].obs, Hamiltonian + ): raise TypeError("ExpectationMP(Hamiltonian) cannot be computed with samples.") - # measure_fn = _measure_hamiltonian_with_samples - if isinstance(group[0], ExpectationMP) and isinstance(group[0].obs, Sum): + if isinstance(group[0], (ExpectationMP, VarianceMP)) and isinstance(group[0].obs, Sum): raise TypeError("ExpectationMP(Sum) cannot be computed with samples.") - # measure_fn = _measure_sum_with_samples if isinstance(group[0], (ClassicalShadowMP, ShadowExpvalMP)): raise TypeError( "ExpectationMP(ClassicalShadowMP, ShadowExpvalMP) cannot be computed with samples." ) - # measure_fn = _measure_classical_shadow all_res.extend(self._measure_with_samples_diagonalizing_gates(group, shots)) flat_indices = [_i for i in indices for _i in i] @@ -347,7 +341,9 @@ def _apply_diagonalizing_gates(self, mps: List[SampleMeasurement], adjoint: bool diagonalizing_gates = [] if adjoint: - diagonalizing_gates = [qml.adjoint(g, lazy=False) for g in diagonalizing_gates] + diagonalizing_gates = [ + qml.adjoint(g, lazy=False) for g in reversed(diagonalizing_gates) + ] self._qubit_state.apply_operations(diagonalizing_gates) diff --git a/pennylane_lightning/lightning_qubit/_state_vector.py b/pennylane_lightning/lightning_qubit/_state_vector.py index 63cb791b8f..f24293b1ac 100644 --- a/pennylane_lightning/lightning_qubit/_state_vector.py +++ b/pennylane_lightning/lightning_qubit/_state_vector.py @@ -29,6 +29,7 @@ import numpy as np import pennylane as qml from pennylane import BasisState, DeviceError, StatePrep +from pennylane.ops.op_math import Adjoint from pennylane.tape import QuantumScript from pennylane.wires import Wires @@ -259,16 +260,20 @@ def _apply_lightning(self, operations): # Skip over identity operations instead of performing # matrix multiplication with it. for operation in operations: - name = operation.name - if name == "Identity": + if isinstance(operation, qml.Identity): continue + if isinstance(operation, Adjoint): + name = operation.base.name + invert_param = True + else: + name = operation.name + invert_param = False method = getattr(state, name, None) wires = list(operation.wires) if method is not None: # apply specialized gate - inv = False param = operation.parameters - method(wires, inv, param) + method(wires, invert_param, param) elif isinstance(operation, qml.ops.Controlled): # apply n-controlled gate self._apply_lightning_controlled(operation) else: # apply gate as a matrix diff --git a/tests/lightning_qubit/test_measurements_class.py b/tests/lightning_qubit/test_measurements_class.py index eae8f1fa44..6717dec859 100644 --- a/tests/lightning_qubit/test_measurements_class.py +++ b/tests/lightning_qubit/test_measurements_class.py @@ -80,7 +80,6 @@ def test_initialization(lightning_sv): m = LightningMeasurements(statevector) assert m.qubit_state is statevector - assert m.state is statevector.state_vector assert m.dtype == statevector.dtype @@ -480,7 +479,7 @@ def test_single_return_value(self, measurement, observable, lightning_sv, tol): assert np.allclose(result, expected, max(tol, 1.0e-5)) @flaky(max_runs=5) - @pytest.mark.parametrize("shots", [None, 100000]) + @pytest.mark.parametrize("shots", [None, 1000000]) @pytest.mark.parametrize("measurement", [qml.expval, qml.probs, qml.var]) @pytest.mark.parametrize( "obs0_", @@ -531,11 +530,6 @@ def test_double_return_value(self, shots, measurement, obs0_, obs1_, lightning_s f"Observable of type {type(obs0_).__name__} is not supported for rotating probabilities." ) - if shots is not None and (isinstance(obs0_, skip_list) or isinstance(obs1_, skip_list)): - pytest.skip( - f"Observable of type {type(obs0_).__name__} is not supported when finite shots." - ) - n_qubits = 4 n_layers = 1 np.random.seed(0) @@ -545,13 +539,29 @@ def test_double_return_value(self, shots, measurement, obs0_, obs1_, lightning_s measurements = [measurement(op=obs0_), measurement(op=obs1_)] tape = qml.tape.QuantumScript(ops, measurements, shots=shots) - expected = self.calculate_reference(tape, lightning_sv) - if len(expected) == 1: - expected = expected[0] statevector = lightning_sv(n_qubits) statevector = statevector.get_final_state(tape) m = LightningMeasurements(statevector) - result = m.measure_final_state(tape) + + skip_list = ( + qml.ops.Sum, + qml.Hamiltonian, + qml.SparseHamiltonian, + ) + if ( + (measurement is qml.expval or measurement is qml.var) + and shots is not None + and (isinstance(obs0_, skip_list) or isinstance(obs1_, skip_list)) + ): + with pytest.raises(TypeError): + _ = m.measure_final_state(tape) + return + else: + result = m.measure_final_state(tape) + + expected = self.calculate_reference(tape, lightning_sv) + if len(expected) == 1: + expected = expected[0] assert isinstance(result, Sequence) assert len(result) == len(expected) From f96b4d7f3783a161cd85c2400e4436bc473e9880 Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Mon, 18 Mar 2024 17:53:14 +0000 Subject: [PATCH 203/428] Call isort/black with python -m --- .github/workflows/format.yml | 4 ++-- requirements-dev.txt | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index 1a69e3023e..c41d4b189b 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -27,10 +27,10 @@ jobs: uses: actions/checkout@v3 - name: Run isort - run: isort --profile black ./pennylane_lightning/ ./mpitests ./tests --check --diff + run: python -m isort --profile black ./pennylane_lightning/ ./mpitests ./tests --check --diff - name: Run Black - run: black -l 100 pennylane_lightning/ tests/ --check --verbose + run: python -m black -l 100 pennylane_lightning/ tests/ --check --verbose format-cpp: name: Format (C++) diff --git a/requirements-dev.txt b/requirements-dev.txt index a4ceae1c8c..e9467a9e54 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -12,7 +12,8 @@ pre-commit>=2.19.0 black==23.7.0 clang-tidy~=16.0 clang-format~=16.0 -isort~=5.13.2 +isort==5.13.2 +click==8.0.4 cmake custatevec-cu12 pylint From de17699751fba6536f133a9afab4b5ba4c110556 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Mon, 18 Mar 2024 15:30:08 -0400 Subject: [PATCH 204/428] update dev version --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index 5bc57662cf..fb85e127e0 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.36.0-dev11" +__version__ = "0.36.0-dev12" From ad66e765c12be2912bc7f46a8f0252c1851a18f4 Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Mon, 18 Mar 2024 20:30:28 +0000 Subject: [PATCH 205/428] Add docstrings, rm C_DTYPE. --- pennylane_lightning/lightning_qubit/_measurements.py | 10 ++++++++++ .../lightning_qubit/lightning_qubit2.py | 8 +++++--- tests/test_expval.py | 7 ++++++- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/_measurements.py b/pennylane_lightning/lightning_qubit/_measurements.py index 9a01188a36..462f382006 100644 --- a/pennylane_lightning/lightning_qubit/_measurements.py +++ b/pennylane_lightning/lightning_qubit/_measurements.py @@ -58,6 +58,16 @@ class LightningMeasurements: Args: qubit_state(LightningStateVector): Lightning state-vector class containing the state vector to be measured. + mcmc (bool): Determine whether to use the approximate Markov Chain Monte Carlo + sampling method when generating samples. + kernel_name (str): name of MCMC transition kernel. The current version supports + two kernels: ``"Local"`` and ``"NonZeroRandom"``. + The local kernel conducts a bit-flip local transition between states. + The local kernel generates a random qubit site and then generates a random + number to determine the new bit at that qubit site. The ``"NonZeroRandom"`` kernel + randomly transits between states that have nonzero probability. + num_burnin (int): number of MCMC steps that will be dropped. Increasing this value will + result in a closer approximation but increased runtime. """ def __init__( diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py index a8d2a38fd2..4a0d8b6200 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit2.py @@ -59,6 +59,9 @@ def simulate(circuit: QuantumScript, state: LightningStateVector, mcmc: dict = N Args: circuit (QuantumTape): The single circuit to simulate state (LightningStateVector): handle to Lightning state vector + mcmc (dict): Dictionary containing the Markov Chain Monte Carlo + parameters: mcmc, kernel_name, num_burnin. Descriptions of + these fields are found in :class:`~.LightningQubit2`. Returns: Tuple[TensorLike]: The results of the simulation @@ -299,13 +302,13 @@ class LightningQubit2(Device): will pull a seed from the OS entropy. mcmc (bool): Determine whether to use the approximate Markov Chain Monte Carlo sampling method when generating samples. - kernel_name (str): name of transition kernel. The current version supports + kernel_name (str): name of transition MCMC kernel. The current version supports two kernels: ``"Local"`` and ``"NonZeroRandom"``. The local kernel conducts a bit-flip local transition between states. The local kernel generates a random qubit site and then generates a random number to determine the new bit at that qubit site. The ``"NonZeroRandom"`` kernel randomly transits between states that have nonzero probability. - num_burnin (int): number of steps that will be dropped. Increasing this value will + num_burnin (int): number of MCMC steps that will be dropped. Increasing this value will result in a closer approximation but increased runtime. batch_obs (bool): Determine whether we process observables in parallel when computing the jacobian. This value is only relevant when the lightning @@ -378,7 +381,6 @@ def c_dtype(self): """State vector complex data type.""" return self._c_dtype - C_DTYPE = c_dtype dtype = c_dtype def _setup_execution_config(self, config): diff --git a/tests/test_expval.py b/tests/test_expval.py index 62ef646229..077911d13d 100644 --- a/tests/test_expval.py +++ b/tests/test_expval.py @@ -86,7 +86,12 @@ def test_paulix_expectation(self, theta, phi, qubit_device, tol): res = np.array([dev.expval(O1), dev.expval(O2)], dtype=dev.C_DTYPE) assert np.allclose( - res, np.array([np.sin(theta) * np.sin(phi), np.sin(phi)], dtype=dev.C_DTYPE), tol * 10 + res, + np.array( + [np.sin(theta) * np.sin(phi), np.sin(phi)], + dtype=dev.c_dtype if ld._new_API else ld._new_API, + ), + tol * 10, ) def test_pauliy_expectation(self, theta, phi, qubit_device, tol): From 70c5494e2b57c2162047061022df4cf6e05c7813 Mon Sep 17 00:00:00 2001 From: Dev version update bot Date: Mon, 18 Mar 2024 23:27:08 +0000 Subject: [PATCH 206/428] Auto update version --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index 5bc57662cf..fb85e127e0 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.36.0-dev11" +__version__ = "0.36.0-dev12" From f91a1d8092c5c24406171d57a21b528269cb2751 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Tue, 19 Mar 2024 07:40:33 -0400 Subject: [PATCH 207/428] comment isort check --- .github/workflows/format.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index c41d4b189b..0de3d16bf3 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -26,8 +26,8 @@ jobs: - name: Checkout PennyLane-Lightning uses: actions/checkout@v3 - - name: Run isort - run: python -m isort --profile black ./pennylane_lightning/ ./mpitests ./tests --check --diff + # - name: Run isort + # run: python -m isort --profile black ./pennylane_lightning/ ./mpitests ./tests --check --diff - name: Run Black run: python -m black -l 100 pennylane_lightning/ tests/ --check --verbose From c52fd1063dbeb4d930a71a50e46c422515583856 Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Tue, 19 Mar 2024 08:26:55 -0400 Subject: [PATCH 208/428] trigger ci From 48730228d44d6387f8c77aeb34818fd735cf762a Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Tue, 19 Mar 2024 09:01:29 -0400 Subject: [PATCH 209/428] Update tests/test_expval.py Co-authored-by: Amintor Dusko <87949283+AmintorDusko@users.noreply.github.com> --- tests/test_expval.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_expval.py b/tests/test_expval.py index 077911d13d..849e664527 100644 --- a/tests/test_expval.py +++ b/tests/test_expval.py @@ -89,7 +89,7 @@ def test_paulix_expectation(self, theta, phi, qubit_device, tol): res, np.array( [np.sin(theta) * np.sin(phi), np.sin(phi)], - dtype=dev.c_dtype if ld._new_API else ld._new_API, + dtype=dev.dtype ), tol * 10, ) From 3481ef224d091fa7bc02d605ca1dcbb048baaf1e Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Tue, 19 Mar 2024 13:18:18 +0000 Subject: [PATCH 210/428] Init mcmc params to None in measurements. --- .../lightning_qubit/_measurements.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/_measurements.py b/pennylane_lightning/lightning_qubit/_measurements.py index 462f382006..aae3d8a20c 100644 --- a/pennylane_lightning/lightning_qubit/_measurements.py +++ b/pennylane_lightning/lightning_qubit/_measurements.py @@ -73,9 +73,9 @@ class LightningMeasurements: def __init__( self, qubit_state: LightningStateVector, - mcmc: bool = False, - kernel_name: str = "Local", - num_burnin: int = 100, + mcmc: bool = None, + kernel_name: str = None, + num_burnin: int = None, ) -> None: self._qubit_state = qubit_state self._dtype = qubit_state.dtype @@ -83,6 +83,10 @@ def __init__( self._mcmc = mcmc self._kernel_name = kernel_name self._num_burnin = num_burnin + if self._mcmc and not self._kernel_name: + self._kernel_name = "Local" + if self._mcmc and not self._num_burnin: + self._num_burnin = 100 @property def qubit_state(self): @@ -329,9 +333,10 @@ def measure_with_samples( ) all_res.extend(self._measure_with_samples_diagonalizing_gates(group, shots)) - flat_indices = [_i for i in indices for _i in i] - # reorder results + flat_indices = [] + for row in indices: + flat_indices += row sorted_res = tuple( res for _, res in sorted(list(enumerate(all_res)), key=lambda r: flat_indices[r[0]]) ) From cad382a67c059f16f40cbd8e31f734ae283b6f2e Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Tue, 19 Mar 2024 13:22:28 +0000 Subject: [PATCH 211/428] Reformat with python3.11 --- .github/workflows/format.yml | 2 +- .../lightning_kokkos/lightning_kokkos.py | 14 +++++++------- .../lightning_qubit/lightning_qubit.py | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index c41d4b189b..91e4143c2b 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -17,7 +17,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v4 with: - python-version: '3.9' + python-version: '3.11' - name: Install dependencies run: diff --git a/pennylane_lightning/lightning_kokkos/lightning_kokkos.py b/pennylane_lightning/lightning_kokkos/lightning_kokkos.py index 499d9a588b..d7821660bf 100644 --- a/pennylane_lightning/lightning_kokkos/lightning_kokkos.py +++ b/pennylane_lightning/lightning_kokkos/lightning_kokkos.py @@ -64,13 +64,6 @@ from pennylane.ops.op_math import Adjoint from pennylane.wires import Wires - # pylint: disable=import-error, no-name-in-module, ungrouped-imports - from pennylane_lightning.core._serialize import ( - QuantumScriptSerializer, - global_phase_diagonal, - ) - from pennylane_lightning.core._version import __version__ - # pylint: disable=import-error, no-name-in-module, ungrouped-imports from pennylane_lightning.lightning_kokkos_ops.algorithms import ( AdjointJacobianC64, @@ -79,6 +72,13 @@ create_ops_listC128, ) + # pylint: disable=import-error, no-name-in-module, ungrouped-imports + from pennylane_lightning.core._serialize import ( + QuantumScriptSerializer, + global_phase_diagonal, + ) + from pennylane_lightning.core._version import __version__ + def _kokkos_dtype(dtype): if dtype not in [np.complex128, np.complex64]: # pragma: no cover raise ValueError(f"Data type is not supported for state-vector computation: {dtype}") diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit.py b/pennylane_lightning/lightning_qubit/lightning_qubit.py index de7c863a85..ffdb5e33a4 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit.py @@ -66,10 +66,6 @@ from pennylane.ops.op_math import Adjoint from pennylane.wires import Wires - # pylint: disable=import-error, no-name-in-module, ungrouped-imports - from pennylane_lightning.core._serialize import QuantumScriptSerializer - from pennylane_lightning.core._version import __version__ - # pylint: disable=no-name-in-module, ungrouped-imports from pennylane_lightning.lightning_qubit_ops.algorithms import ( AdjointJacobianC64, @@ -80,6 +76,10 @@ create_ops_listC128, ) + # pylint: disable=import-error, no-name-in-module, ungrouped-imports + from pennylane_lightning.core._serialize import QuantumScriptSerializer + from pennylane_lightning.core._version import __version__ + def _state_dtype(dtype): if dtype not in [np.complex128, np.complex64]: # pragma: no cover raise ValueError(f"Data type is not supported for state-vector computation: {dtype}") From c1755721e17480995264b372f644b67a1c33bcfb Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Tue, 19 Mar 2024 13:23:49 +0000 Subject: [PATCH 212/428] Reformat black --- tests/test_expval.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/test_expval.py b/tests/test_expval.py index 849e664527..9725028014 100644 --- a/tests/test_expval.py +++ b/tests/test_expval.py @@ -87,10 +87,7 @@ def test_paulix_expectation(self, theta, phi, qubit_device, tol): res = np.array([dev.expval(O1), dev.expval(O2)], dtype=dev.C_DTYPE) assert np.allclose( res, - np.array( - [np.sin(theta) * np.sin(phi), np.sin(phi)], - dtype=dev.dtype - ), + np.array([np.sin(theta) * np.sin(phi), np.sin(phi)], dtype=dev.dtype), tol * 10, ) From 16d019e1c646275596daccbca716f90221bf6d0b Mon Sep 17 00:00:00 2001 From: Dev version update bot Date: Tue, 19 Mar 2024 14:00:59 +0000 Subject: [PATCH 213/428] Auto update version --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index fb85e127e0..000566162d 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.36.0-dev12" +__version__ = "0.36.0-dev13" From 2cf45e9e699b9640458ab03a52fce76c78efa6ce Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Tue, 19 Mar 2024 11:02:52 -0400 Subject: [PATCH 214/428] update QuantumScriptSerializer --- pennylane_lightning/core/_serialize.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_serialize.py b/pennylane_lightning/core/_serialize.py index 502456533a..fb9ff6dd82 100644 --- a/pennylane_lightning/core/_serialize.py +++ b/pennylane_lightning/core/_serialize.py @@ -62,7 +62,7 @@ def __init__( self.use_csingle = use_csingle self.device_name = device_name self.split_obs = split_obs - if device_name in ("lightning.qubit", "lightning.qubit2"): + if device_name == "lightning.qubit": try: import pennylane_lightning.lightning_qubit_ops as lightning_ops except ImportError as exception: From f63d6af6d1aaf0df6bb860f42b210bb08fd27919 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Tue, 19 Mar 2024 11:03:34 -0400 Subject: [PATCH 215/428] remove LightningQubit2 from init --- pennylane_lightning/lightning_qubit/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pennylane_lightning/lightning_qubit/__init__.py b/pennylane_lightning/lightning_qubit/__init__.py index 017f7fedec..a1b792afde 100644 --- a/pennylane_lightning/lightning_qubit/__init__.py +++ b/pennylane_lightning/lightning_qubit/__init__.py @@ -16,4 +16,3 @@ from pennylane_lightning.core import __version__ from .lightning_qubit import LightningQubit -from .lightning_qubit2 import LightningQubit2 From 080d03796a6ccfe8e088560bad9d6aceba76d0e4 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Tue, 19 Mar 2024 11:06:25 -0400 Subject: [PATCH 216/428] update setup.py --- setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.py b/setup.py index 5e37d8e2b2..977f753559 100644 --- a/setup.py +++ b/setup.py @@ -186,7 +186,6 @@ def build_extension(self, ext: CMakeExtension): suffix = suffix[0].upper() + suffix[1:] pennylane_plugins = [device_name + " = pennylane_lightning." + backend + ":Lightning" + suffix] -pennylane_plugins.append("lightning.qubit2 = pennylane_lightning.lightning_qubit.lightning_qubit2:LightningQubit2") pkg_suffix = "" if suffix == "Qubit" else "_" + suffix From c5442a6d565fc9cee73be47d161b288f5fd48853 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Tue, 19 Mar 2024 11:07:36 -0400 Subject: [PATCH 217/428] remove lightning.qubit2 from tests configuration --- tests/conftest.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 40b4c56eb3..1cc9480cb4 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -89,7 +89,7 @@ def n_subsystems(request): # Looking for the device for testing. default_device = "lightning.qubit" -supported_devices = {"lightning.kokkos", "lightning.qubit", "lightning.qubit2", "lightning.gpu"} +supported_devices = {"lightning.kokkos", "lightning.qubit", "lightning.gpu"} supported_devices.update({sb.replace(".", "_") for sb in supported_devices}) @@ -97,7 +97,7 @@ def get_device(): """Return the pennylane lightning device. The device is ``lightning.qubit`` by default. Allowed values are: - "lightning.kokkos", "lightning.qubit2", and "lightning.qubit". An + "lightning.kokkos", and "lightning.qubit". An underscore can also be used instead of a dot. If the environment variable ``PL_DEVICE`` is defined, its value is used. Underscores are replaced by dots upon exiting. @@ -133,11 +133,6 @@ def get_device(): if hasattr(pennylane_lightning, "lightning_gpu_ops"): import pennylane_lightning.lightning_gpu_ops as lightning_ops -elif device_name == "lightning.qubit2": - from pennylane_lightning.lightning_qubit import LightningQubit2 as LightningDevice - - if hasattr(pennylane_lightning, "lightning_qubit_ops"): - import pennylane_lightning.lightning_qubit_ops as lightning_ops else: from pennylane_lightning.lightning_qubit import LightningQubit as LightningDevice From 83bf167bcb571ef1f6866846e021c620d5bee5b9 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Tue, 19 Mar 2024 11:08:22 -0400 Subject: [PATCH 218/428] remove extra tests for lightning.qubit2 --- .github/workflows/tests_linux.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/tests_linux.yml b/.github/workflows/tests_linux.yml index 4a0c85bd9e..f1641b27d2 100644 --- a/.github/workflows/tests_linux.yml +++ b/.github/workflows/tests_linux.yml @@ -185,7 +185,6 @@ jobs: cd main/ DEVICENAME=`echo ${{ matrix.pl_backend }} | sed "s/_/./g"` PL_DEVICE=${DEVICENAME} python -m pytest tests/ $COVERAGE_FLAGS - PL_DEVICE=lightning_qubit2 python -m pytest tests/ $COVERAGE_FLAGS --cov-append pl-device-test --device ${DEVICENAME} --skip-ops --shots=20000 $COVERAGE_FLAGS --cov-append pl-device-test --device ${DEVICENAME} --shots=None --skip-ops $COVERAGE_FLAGS --cov-append mv .coverage .coverage-${{ github.job }}-${{ matrix.pl_backend }} From 3d831d04cc84ad223cd4249130294ddba5dd0c99 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Tue, 19 Mar 2024 11:10:02 -0400 Subject: [PATCH 219/428] migrate lightning.qubit2 to lightning.qubit on tests --- tests/lightning_qubit/test_measurements_samples_MCMC.py | 2 +- tests/lightning_qubit2/test_new_api_device.py | 2 +- tests/test_adjoint_jacobian.py | 4 ++-- tests/test_gates.py | 8 ++++---- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/lightning_qubit/test_measurements_samples_MCMC.py b/tests/lightning_qubit/test_measurements_samples_MCMC.py index bf2104364b..562c705dbe 100644 --- a/tests/lightning_qubit/test_measurements_samples_MCMC.py +++ b/tests/lightning_qubit/test_measurements_samples_MCMC.py @@ -20,7 +20,7 @@ from conftest import LightningDevice as ld from conftest import device_name -if device_name not in ("lightning.qubit", "lightning.qubit2"): +if device_name != "lightning.qubit": pytest.skip("Exclusive tests for lightning.qubit. Skipping.", allow_module_level=True) if not ld._CPP_BINARY_AVAILABLE: diff --git a/tests/lightning_qubit2/test_new_api_device.py b/tests/lightning_qubit2/test_new_api_device.py index c121c28158..6407ed60b5 100644 --- a/tests/lightning_qubit2/test_new_api_device.py +++ b/tests/lightning_qubit2/test_new_api_device.py @@ -24,7 +24,7 @@ from pennylane.devices.default_qubit import adjoint_ops from pennylane.tape import QuantumScript -from pennylane_lightning.lightning_qubit.lightning_qubit2 import ( +from pennylane_lightning.lightning_qubit.lightning_qubit import ( _add_adjoint_transforms, _supports_adjoint, accepted_observables, diff --git a/tests/test_adjoint_jacobian.py b/tests/test_adjoint_jacobian.py index 4fc5058c4c..463f648cd8 100644 --- a/tests/test_adjoint_jacobian.py +++ b/tests/test_adjoint_jacobian.py @@ -272,7 +272,7 @@ def test_Rot_gradient(self, stateprep, theta, dev): assert np.allclose(calculated_val, numeric_val, atol=tol, rtol=0) @pytest.mark.skipif( - device_name not in ("lightning.qubit", "lightning.qubit2") or not ld._CPP_BINARY_AVAILABLE, + device_name != "lightning.qubit" or not ld._CPP_BINARY_AVAILABLE, reason="N-controlled operations only implemented in lightning.qubit.", ) @pytest.mark.parametrize("n_qubits", [1, 2, 3, 4]) @@ -826,7 +826,7 @@ def circuit(p): assert np.allclose(jac_ad, jac_bp, atol=tol, rtol=0) @pytest.mark.skipif( - device_name not in ("lightning.qubit", "lightning.qubit2") or not ld._CPP_BINARY_AVAILABLE, + device_name != "lightning.qubit" or not ld._CPP_BINARY_AVAILABLE, reason="N-controlled operations only implemented in lightning.qubit.", ) @pytest.mark.parametrize( diff --git a/tests/test_gates.py b/tests/test_gates.py index 3cf15c0513..884ecc5bca 100644 --- a/tests/test_gates.py +++ b/tests/test_gates.py @@ -326,7 +326,7 @@ def circuit(): @pytest.mark.skipif( - device_name not in ("lightning.qubit", "lightning.qubit2"), + device_name != "lightning.qubit", reason="N-controlled operations only implemented in lightning.qubit.", ) @pytest.mark.parametrize("control_value", [False, True]) @@ -369,7 +369,7 @@ def circuit(): @pytest.mark.skipif( - device_name not in ("lightning.qubit", "lightning.qubit2"), + device_name != "lightning.qubit", reason="N-controlled operations only implemented in lightning.qubit.", ) @pytest.mark.parametrize( @@ -446,7 +446,7 @@ def circuit(): @pytest.mark.skipif( - device_name not in ("lightning.qubit", "lightning.qubit2"), + device_name != "lightning.qubit", reason="N-controlled operations only implemented in lightning.qubit.", ) def test_controlled_qubit_unitary_from_op(tol): @@ -467,7 +467,7 @@ def circuit(x): @pytest.mark.skipif( - device_name not in ("lightning.qubit", "lightning.qubit2"), + device_name != "lightning.qubit", reason="N-controlled operations only implemented in lightning.qubit.", ) @pytest.mark.parametrize("control_wires", range(4)) From c30f94f06f6369d8f2ddcc645ec63e375d4d3975 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Tue, 19 Mar 2024 11:10:38 -0400 Subject: [PATCH 220/428] make lightning.qubit2 the new lightning.qubit --- .../lightning_qubit/lightning_qubit.py | 1129 ++++++----------- .../lightning_qubit/lightning_qubit2.py | 527 -------- 2 files changed, 365 insertions(+), 1291 deletions(-) delete mode 100644 pennylane_lightning/lightning_qubit/lightning_qubit2.py diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit.py b/pennylane_lightning/lightning_qubit/lightning_qubit.py index ffdb5e33a4..200c7584f7 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit.py @@ -1,4 +1,4 @@ -# Copyright 2018-2023 Xanadu Quantum Technologies Inc. +# Copyright 2018-2024 Xanadu Quantum Technologies Inc. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -11,81 +11,112 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. - -r""" -This module contains the :class:`~.LightningQubit` class, a PennyLane simulator device that -interfaces with C++ for fast linear algebra calculations. """ - +This module contains the LightningQubit class that inherits from the new device interface. +""" +from dataclasses import replace from pathlib import Path -from typing import List, Sequence -from warnings import warn +from typing import Callable, Optional, Sequence, Union import numpy as np - -from pennylane_lightning.core.lightning_base import ( - LightningBase, - LightningBaseFallBack, - _chunk_iterable, +import pennylane as qml +from pennylane.devices import DefaultExecutionConfig, Device, ExecutionConfig +from pennylane.devices.default_qubit import adjoint_ops +from pennylane.devices.modifiers import simulator_tracking, single_tape_support +from pennylane.devices.preprocess import ( + decompose, + no_sampling, + validate_adjoint_trainable_params, + validate_device_wires, + validate_measurements, + validate_observables, ) +from pennylane.tape import QuantumScript, QuantumTape +from pennylane.transforms.core import TransformProgram +from pennylane.typing import Result, ResultBatch + +from ._adjoint_jacobian import LightningAdjointJacobian +from ._measurements import LightningMeasurements +from ._state_vector import LightningStateVector try: - # pylint: disable=import-error, no-name-in-module - from pennylane_lightning.lightning_qubit_ops import ( - MeasurementsC64, - MeasurementsC128, - StateVectorC64, - StateVectorC128, - allocate_aligned_array, - backend_info, - best_alignment, - get_alignment, - ) + # pylint: disable=import-error, unused-import + from pennylane_lightning.lightning_qubit_ops import backend_info LQ_CPP_BINARY_AVAILABLE = True except ImportError: LQ_CPP_BINARY_AVAILABLE = False -if LQ_CPP_BINARY_AVAILABLE: - from os import getenv +Result_or_ResultBatch = Union[Result, ResultBatch] +QuantumTapeBatch = Sequence[QuantumTape] +QuantumTape_or_Batch = Union[QuantumTape, QuantumTapeBatch] +PostprocessingFn = Callable[[ResultBatch], Result_or_ResultBatch] - import pennylane as qml - # pylint: disable=ungrouped-imports - from pennylane import ( - BasisState, - DeviceError, - Projector, - QuantumFunctionError, - Rot, - StatePrep, - math, - ) - from pennylane.measurements import Expectation, MeasurementProcess, State - from pennylane.operation import Tensor - from pennylane.ops.op_math import Adjoint - from pennylane.wires import Wires - - # pylint: disable=no-name-in-module, ungrouped-imports - from pennylane_lightning.lightning_qubit_ops.algorithms import ( - AdjointJacobianC64, - AdjointJacobianC128, - VectorJacobianProductC64, - VectorJacobianProductC128, - create_ops_listC64, - create_ops_listC128, - ) +def simulate(circuit: QuantumScript, state: LightningStateVector, mcmc: dict = None) -> Result: + """Simulate a single quantum script. + + Args: + circuit (QuantumTape): The single circuit to simulate + state (LightningStateVector): handle to Lightning state vector + mcmc (dict): Dictionary containing the Markov Chain Monte Carlo + parameters: mcmc, kernel_name, num_burnin. Descriptions of + these fields are found in :class:`~.LightningQubit`. + + Returns: + Tuple[TensorLike]: The results of the simulation + + Note that this function can return measurements for non-commuting observables simultaneously. + """ + if mcmc is None: + mcmc = {} + state.reset_state() + final_state = state.get_final_state(circuit) + return LightningMeasurements(final_state, **mcmc).measure_final_state(circuit) + + +def jacobian(circuit: QuantumTape, state: LightningStateVector, batch_obs=False): + """Compute the Jacobian for a single quantum script. + + Args: + circuit (QuantumTape): The single circuit to simulate + state (LightningStateVector): handle to Lightning state vector + batch_obs (bool): Determine whether we process observables in parallel when + computing the jacobian. This value is only relevant when the lightning + qubit is built with OpenMP. Default is False. + + Returns: + TensorLike: The Jacobian of the quantum script + """ + circuit = circuit.map_to_standard_wires() + state.reset_state() + final_state = state.get_final_state(circuit) + return LightningAdjointJacobian(final_state, batch_obs=batch_obs).calculate_jacobian(circuit) + - # pylint: disable=import-error, no-name-in-module, ungrouped-imports - from pennylane_lightning.core._serialize import QuantumScriptSerializer - from pennylane_lightning.core._version import __version__ +def simulate_and_jacobian(circuit: QuantumTape, state: LightningStateVector, batch_obs=False): + """Simulate a single quantum script and compute its Jacobian. - def _state_dtype(dtype): - if dtype not in [np.complex128, np.complex64]: # pragma: no cover - raise ValueError(f"Data type is not supported for state-vector computation: {dtype}") - return StateVectorC128 if dtype == np.complex128 else StateVectorC64 + Args: + circuit (QuantumTape): The single circuit to simulate + state (LightningStateVector): handle to Lightning state vector + batch_obs (bool): Determine whether we process observables in parallel when + computing the jacobian. This value is only relevant when the lightning + qubit is built with OpenMP. Default is False. - allowed_operations = { + Returns: + Tuple[TensorLike]: The results of the simulation and the calculated Jacobian + + Note that this function can return measurements for non-commuting observables simultaneously. + """ + circuit = circuit.map_to_standard_wires() + res = simulate(circuit, state) + jac = LightningAdjointJacobian(state, batch_obs=batch_obs).calculate_jacobian(circuit) + return res, jac + + +_operations = frozenset( + { "Identity", "BasisState", "QubitStateVector", @@ -170,8 +201,12 @@ def _state_dtype(dtype): "ECR", "BlockEncode", } +) +# The set of supported operations. - allowed_observables = { + +_observables = frozenset( + { "PauliX", "PauliY", "PauliZ", @@ -186,741 +221,307 @@ def _state_dtype(dtype): "Prod", "Exp", } +) +# The set of supported observables. - class LightningQubit(LightningBase): - """PennyLane Lightning Qubit device. - A device that interfaces with C++ to perform fast linear algebra calculations. +def stopping_condition(op: qml.operation.Operator) -> bool: + """A function that determines whether or not an operation is supported by ``lightning.qubit``.""" + return op.name in _operations - Use of this device requires pre-built binaries or compilation from source. Check out the - :doc:`/lightning_qubit/installation` guide for more details. - Args: - wires (int): the number of wires to initialize the device with - c_dtype: Datatypes for statevector representation. Must be one of - ``np.complex64`` or ``np.complex128``. - shots (int): How many times the circuit should be evaluated (or sampled) to estimate - the expectation values. Defaults to ``None`` if not specified. Setting - to ``None`` results in computing statistics like expectation values and - variances analytically. - mcmc (bool): Determine whether to use the approximate Markov Chain Monte Carlo - sampling method when generating samples. - kernel_name (str): name of transition kernel. The current version supports - two kernels: ``"Local"`` and ``"NonZeroRandom"``. - The local kernel conducts a bit-flip local transition between states. - The local kernel generates a random qubit site and then generates a random - number to determine the new bit at that qubit site. The ``"NonZeroRandom"`` kernel - randomly transits between states that have nonzero probability. - num_burnin (int): number of steps that will be dropped. Increasing this value will - result in a closer approximation but increased runtime. - batch_obs (bool): Determine whether we process observables in parallel when - computing the jacobian. This value is only relevant when the lightning - qubit is built with OpenMP. - """ +def accepted_observables(obs: qml.operation.Operator) -> bool: + """A function that determines whether or not an observable is supported by ``lightning.qubit``.""" + return obs.name in _observables - name = "Lightning Qubit PennyLane plugin" - short_name = "lightning.qubit" - operations = allowed_operations - observables = allowed_observables - _backend_info = backend_info - config = Path(__file__).parent / "lightning_qubit.toml" - - def __init__( # pylint: disable=too-many-arguments - self, - wires, - *, - c_dtype=np.complex128, - shots=None, - mcmc=False, - kernel_name="Local", - num_burnin=100, - batch_obs=False, - ): - super().__init__(wires, shots=shots, c_dtype=c_dtype) - - # Create the initial state. Internally, we store the - # state as an array of dimension [2]*wires. - self._qubit_state = _state_dtype(c_dtype)(self.num_wires) - self._batch_obs = batch_obs - self._mcmc = mcmc - if self._mcmc: - if kernel_name not in [ - "Local", - "NonZeroRandom", - ]: - raise NotImplementedError( - f"The {kernel_name} is not supported and currently " - "only 'Local' and 'NonZeroRandom' kernels are supported." - ) - shots = shots if isinstance(shots, Sequence) else [shots] - if any(num_burnin >= s for s in shots): - raise ValueError("Shots should be greater than num_burnin.") - self._kernel_name = kernel_name - self._num_burnin = num_burnin - - @staticmethod - def _asarray(arr, dtype=None): - arr = np.asarray(arr) # arr is not copied - - if arr.dtype.kind not in ["f", "c"]: - return arr - - if not dtype: - dtype = arr.dtype - - # We allocate a new aligned memory and copy data to there if alignment or dtype - # mismatches - # Note that get_alignment does not necessarily return CPUMemoryModel(Unaligned) - # numpy allocated memory as the memory location happens to be aligned. - if int(get_alignment(arr)) < int(best_alignment()) or arr.dtype != dtype: - new_arr = allocate_aligned_array(arr.size, np.dtype(dtype), False).reshape( - arr.shape - ) - if len(arr.shape): - new_arr[:] = arr - else: - np.copyto(new_arr, arr) - arr = new_arr - return arr - - def _create_basis_state(self, index): - """Return a computational basis state over all wires. - Args: - index (int): integer representing the computational basis state. - """ - self._qubit_state.setBasisState(index) - - def reset(self): - """Reset the device""" - super().reset() - - # init the state vector to |00..0> - self._qubit_state.resetStateVector() - - @property - def create_ops_list(self): - """Returns create_ops_list function matching ``use_csingle`` precision.""" - return create_ops_listC64 if self.use_csingle else create_ops_listC128 - - @property - def measurements(self): - """Returns a Measurements object matching ``use_csingle`` precision.""" - return ( - MeasurementsC64(self.state_vector) - if self.use_csingle - else MeasurementsC128(self.state_vector) - ) - @property - def state(self): - """Copy the state vector data to a numpy array. - - **Example** - - >>> dev = qml.device('lightning.kokkos', wires=1) - >>> dev.apply([qml.PauliX(wires=[0])]) - >>> print(dev.state) - [0.+0.j 1.+0.j] - """ - state = np.zeros(2**self.num_wires, dtype=self.C_DTYPE) - state = self._asarray(state, dtype=self.C_DTYPE) - self._qubit_state.getState(state) - return state - - @property - def state_vector(self): - """Returns a handle to the statevector.""" - return self._qubit_state - - def _apply_state_vector(self, state, device_wires: Wires): - """Initialize the internal state vector in a specified state. - Args: - state (array[complex]): normalized input state of length ``2**len(wires)`` - or broadcasted state of shape ``(batch_size, 2**len(wires))`` - device_wires (Wires): wires that get initialized in the state - """ - - if isinstance(state, self._qubit_state.__class__): - state_data = allocate_aligned_array(state.size, np.dtype(self.C_DTYPE), True) - state.getState(state_data) - state = state_data - - ravelled_indices, state = self._preprocess_state_vector(state, device_wires) - - # translate to wire labels used by device - device_wires = self.map_wires(device_wires) - output_shape = [2] * self.num_wires - - if len(device_wires) == self.num_wires and Wires(sorted(device_wires)) == device_wires: - # Initialize the entire device state with the input state - state = self._reshape(state, output_shape).ravel(order="C") - self._qubit_state.UpdateData(state) - return - - self._qubit_state.setStateVector(ravelled_indices, state) # this operation on device - - def _apply_basis_state(self, state, wires): - """Initialize the state vector in a specified computational basis state. - - Args: - state (array[int]): computational basis state of shape ``(wires,)`` - consisting of 0s and 1s. - wires (Wires): wires that the provided computational state should be - initialized on - - Note: This function does not support broadcasted inputs yet. - """ - num = self._get_basis_state_index(state, wires) - self._create_basis_state(num) - - def _apply_lightning_controlled(self, operation): - """Apply an arbitrary controlled operation to the state tensor. - - Args: - operation (~pennylane.operation.Operation): operation to apply - - Returns: - array[complex]: the output state tensor - """ - state = self.state_vector - - basename = "PauliX" if operation.name == "MultiControlledX" else operation.base.name - if basename == "Identity": - return - method = getattr(state, f"{basename}", None) - control_wires = self.wires.indices(operation.control_wires) - control_values = ( - [bool(int(i)) for i in operation.hyperparameters["control_values"]] - if operation.name == "MultiControlledX" - else operation.control_values - ) - if operation.name == "MultiControlledX": - target_wires = list(set(self.wires.indices(operation.wires)) - set(control_wires)) - else: - target_wires = self.wires.indices(operation.target_wires) - if method is not None: # apply n-controlled specialized gate - inv = False - param = operation.parameters - method(control_wires, control_values, target_wires, inv, param) - else: # apply gate as an n-controlled matrix - method = getattr(state, "applyControlledMatrix") - target_wires = self.wires.indices(operation.target_wires) - try: - method( - qml.matrix(operation.base), - control_wires, - control_values, - target_wires, - False, - ) - except AttributeError: # pragma: no cover - # To support older versions of PL - method( - operation.base.matrix, control_wires, control_values, target_wires, False - ) - - def apply_lightning(self, operations): - """Apply a list of operations to the state tensor. - - Args: - operations (list[~pennylane.operation.Operation]): operations to apply - - Returns: - array[complex]: the output state tensor - """ - state = self.state_vector - - # Skip over identity operations instead of performing - # matrix multiplication with it. - for operation in operations: - if isinstance(operation, Adjoint): - name = operation.base.name - invert_param = True - else: - name = operation.name - invert_param = False - if name == "Identity": - continue - method = getattr(state, name, None) - wires = self.wires.indices(operation.wires) - - if method is not None: # apply specialized gate - param = operation.parameters - method(wires, invert_param, param) - elif ( - name[0:2] == "C(" - or name == "ControlledQubitUnitary" - or name == "MultiControlledX" - ): # apply n-controlled gate - self._apply_lightning_controlled(operation) - else: # apply gate as a matrix - # Inverse can be set to False since qml.matrix(operation) is already in - # inverted form - method = getattr(state, "applyMatrix") - try: - method(qml.matrix(operation), wires, False) - except AttributeError: # pragma: no cover - # To support older versions of PL - method(operation.matrix, wires, False) - - # pylint: disable=unused-argument - def apply(self, operations, rotations=None, **kwargs): - """Applies operations to the state vector.""" - # State preparation is currently done in Python - if operations: # make sure operations[0] exists - if isinstance(operations[0], StatePrep): - self._apply_state_vector( - operations[0].parameters[0].copy(), operations[0].wires - ) - operations = operations[1:] - elif isinstance(operations[0], BasisState): - self._apply_basis_state(operations[0].parameters[0], operations[0].wires) - operations = operations[1:] - - for operation in operations: - if isinstance(operation, (StatePrep, BasisState)): - raise DeviceError( - f"Operation {operation.name} cannot be used after other " - f"Operations have already been applied on a {self.short_name} device." - ) - - self.apply_lightning(operations) - - # pylint: disable=protected-access - def expval(self, observable, shot_range=None, bin_size=None): - """Expectation value of the supplied observable. - - Args: - observable: A PennyLane observable. - shot_range (tuple[int]): 2-tuple of integers specifying the range of samples - to use. If not specified, all samples are used. - bin_size (int): Divides the shot range into bins of size ``bin_size``, and - returns the measurement statistic separately over each bin. If not - provided, the entire shot range is treated as a single bin. - - Returns: - Expectation value of the observable - """ - if observable.name in [ - "Projector", - ]: - diagonalizing_gates = observable.diagonalizing_gates() - if self.shots is None and diagonalizing_gates: - self.apply(diagonalizing_gates) - results = super().expval(observable, shot_range=shot_range, bin_size=bin_size) - if self.shots is None and diagonalizing_gates: - self.apply([qml.adjoint(g, lazy=False) for g in reversed(diagonalizing_gates)]) - return results - - if self.shots is not None: - # estimate the expectation value - # LightningQubit doesn't support sampling yet - samples = self.sample(observable, shot_range=shot_range, bin_size=bin_size) - return np.squeeze(np.mean(samples, axis=0)) - - measurements = ( - MeasurementsC64(self.state_vector) - if self.use_csingle - else MeasurementsC128(self.state_vector) - ) - if observable.name == "SparseHamiltonian": - csr_hamiltonian = observable.sparse_matrix(wire_order=self.wires).tocsr(copy=False) - return measurements.expval( - csr_hamiltonian.indptr, - csr_hamiltonian.indices, - csr_hamiltonian.data, - ) +def adjoint_measurements(mp: qml.measurements.MeasurementProcess) -> bool: + """Specifies whether or not an observable is compatible with adjoint differentiation on DefaultQubit.""" + return isinstance(mp, qml.measurements.ExpectationMP) - if ( - observable.name in ["Hamiltonian", "Hermitian"] - or (observable.arithmetic_depth > 0) - or isinstance(observable.name, List) - ): - ob_serialized = QuantumScriptSerializer(self.short_name, self.use_csingle)._ob( - observable, self.wire_map - ) - return measurements.expval(ob_serialized) - # translate to wire labels used by device - observable_wires = self.map_wires(observable.wires) +def _supports_adjoint(circuit): + if circuit is None: + return True - return measurements.expval(observable.name, observable_wires) + prog = TransformProgram() + _add_adjoint_transforms(prog) - def var(self, observable, shot_range=None, bin_size=None): - """Variance of the supplied observable. + try: + prog((circuit,)) + except (qml.operation.DecompositionUndefinedError, qml.DeviceError): + return False + return True - Args: - observable: A PennyLane observable. - shot_range (tuple[int]): 2-tuple of integers specifying the range of samples - to use. If not specified, all samples are used. - bin_size (int): Divides the shot range into bins of size ``bin_size``, and - returns the measurement statistic separately over each bin. If not - provided, the entire shot range is treated as a single bin. - Returns: - Variance of the observable - """ - if observable.name in [ - "Projector", - ]: - diagonalizing_gates = observable.diagonalizing_gates() - if self.shots is None and diagonalizing_gates: - self.apply(diagonalizing_gates) - results = super().var(observable, shot_range=shot_range, bin_size=bin_size) - if self.shots is None and diagonalizing_gates: - self.apply([qml.adjoint(g, lazy=False) for g in reversed(diagonalizing_gates)]) - return results - - if self.shots is not None: - # estimate the var - # LightningQubit doesn't support sampling yet - samples = self.sample(observable, shot_range=shot_range, bin_size=bin_size) - return np.squeeze(np.var(samples, axis=0)) - - measurements = ( - MeasurementsC64(self.state_vector) - if self.use_csingle - else MeasurementsC128(self.state_vector) - ) +def _add_adjoint_transforms(program: TransformProgram) -> None: + """Private helper function for ``preprocess`` that adds the transforms specific + for adjoint differentiation. - if observable.name == "SparseHamiltonian": - csr_hamiltonian = observable.sparse_matrix(wire_order=self.wires).tocsr(copy=False) - return measurements.var( - csr_hamiltonian.indptr, - csr_hamiltonian.indices, - csr_hamiltonian.data, - ) + Args: + program (TransformProgram): where we will add the adjoint differentiation transforms - if ( - observable.name in ["Hamiltonian", "Hermitian"] - or (observable.arithmetic_depth > 0) - or isinstance(observable.name, List) - ): - ob_serialized = QuantumScriptSerializer(self.short_name, self.use_csingle)._ob( - observable, self.wire_map - ) - return measurements.var(ob_serialized) + Side Effects: + Adds transforms to the input program. - # translate to wire labels used by device - observable_wires = self.map_wires(observable.wires) + """ - return measurements.var(observable.name, observable_wires) + name = "adjoint + lightning.qubit" + program.add_transform(no_sampling, name=name) + program.add_transform(decompose, stopping_condition=adjoint_ops, name=name) + program.add_transform(validate_observables, accepted_observables, name=name) + program.add_transform( + validate_measurements, analytic_measurements=adjoint_measurements, name=name + ) + program.add_transform(qml.transforms.broadcast_expand) + program.add_transform(validate_adjoint_trainable_params) + + +@simulator_tracking +@single_tape_support +class LightningQubit(Device): + """PennyLane Lightning Qubit device. + + A device that interfaces with C++ to perform fast linear algebra calculations. + + Use of this device requires pre-built binaries or compilation from source. Check out the + :doc:`/lightning_qubit/installation` guide for more details. + + Args: + wires (int): the number of wires to initialize the device with + c_dtype: Datatypes for statevector representation. Must be one of + ``np.complex64`` or ``np.complex128``. + shots (int): How many times the circuit should be evaluated (or sampled) to estimate + the expectation values. Defaults to ``None`` if not specified. Setting + to ``None`` results in computing statistics like expectation values and + variances analytically. + seed (Union[str, None, int, array_like[int], SeedSequence, BitGenerator, Generator]): A + seed-like parameter matching that of ``seed`` for ``numpy.random.default_rng``, or + a request to seed from numpy's global random number generator. + The default, ``seed="global"`` pulls a seed from NumPy's global generator. ``seed=None`` + will pull a seed from the OS entropy. + mcmc (bool): Determine whether to use the approximate Markov Chain Monte Carlo + sampling method when generating samples. + kernel_name (str): name of transition MCMC kernel. The current version supports + two kernels: ``"Local"`` and ``"NonZeroRandom"``. + The local kernel conducts a bit-flip local transition between states. + The local kernel generates a random qubit site and then generates a random + number to determine the new bit at that qubit site. The ``"NonZeroRandom"`` kernel + randomly transits between states that have nonzero probability. + num_burnin (int): number of MCMC steps that will be dropped. Increasing this value will + result in a closer approximation but increased runtime. + batch_obs (bool): Determine whether we process observables in parallel when + computing the jacobian. This value is only relevant when the lightning + qubit is built with OpenMP. + """ + + _device_options = ("rng", "c_dtype", "batch_obs", "mcmc", "kernel_name", "num_burnin") + _CPP_BINARY_AVAILABLE = LQ_CPP_BINARY_AVAILABLE + _new_API = True + _backend_info = backend_info if LQ_CPP_BINARY_AVAILABLE else None + _config = Path(__file__).parent / "lightning_qubit.toml" + + # TODO: Move supported ops/obs to TOML file + operations = _operations + # The names of the supported operations. + + observables = _observables + # The names of the supported observables. + + def __init__( # pylint: disable=too-many-arguments + self, + wires, + *, + c_dtype=np.complex128, + shots=None, + seed="global", + mcmc=False, + kernel_name="Local", + num_burnin=100, + batch_obs=False, + ): + if not self._CPP_BINARY_AVAILABLE: + raise ImportError( + "Pre-compiled binaries for lightning.qubit are not available. " + "To manually compile from source, follow the instructions at " + "https://pennylane-lightning.readthedocs.io/en/latest/installation.html." + ) - def generate_samples(self): - """Generate samples + super().__init__(wires=wires, shots=shots) - Returns: - array[int]: array of samples in binary representation with shape - ``(dev.shots, dev.num_wires)`` - """ - measurements = ( - MeasurementsC64(self.state_vector) - if self.use_csingle - else MeasurementsC128(self.state_vector) - ) - if self._mcmc: - return measurements.generate_mcmc_samples( - len(self.wires), self._kernel_name, self._num_burnin, self.shots - ).astype(int, copy=False) - return measurements.generate_samples(len(self.wires), self.shots).astype( - int, copy=False - ) + self._statevector = LightningStateVector(num_wires=len(self.wires), dtype=c_dtype) - def probability_lightning(self, wires): - """Return the probability of each computational basis state. - - Args: - wires (Iterable[Number, str], Number, str, Wires): wires to return - marginal probabilities for. Wires not provided are traced out of the system. - - Returns: - array[float]: list of the probabilities - """ - return ( - MeasurementsC64(self.state_vector) - if self.use_csingle - else MeasurementsC128(self.state_vector) - ).probs(wires) - - # pylint: disable=attribute-defined-outside-init - def sample(self, observable, shot_range=None, bin_size=None, counts=False): - """Return samples of an observable.""" - diagonalizing_gates = observable.diagonalizing_gates() - if diagonalizing_gates: - self.apply(diagonalizing_gates) - if not isinstance(observable, qml.PauliZ): - self._samples = self.generate_samples() - results = super().sample( - observable, shot_range=shot_range, bin_size=bin_size, counts=counts - ) - if diagonalizing_gates: - self.apply([qml.adjoint(g, lazy=False) for g in reversed(diagonalizing_gates)]) - return results - - @staticmethod - def _check_adjdiff_supported_measurements( - measurements: List[MeasurementProcess], - ): - """Check whether given list of measurement is supported by adjoint_differentiation. - - Args: - measurements (List[MeasurementProcess]): a list of measurement processes to check. - - Returns: - Expectation or State: a common return type of measurements. - """ - if not measurements: - return None - - if len(measurements) == 1 and measurements[0].return_type is State: - return State - - # Now the return_type of measurement processes must be expectation - if any(measurement.return_type is not Expectation for measurement in measurements): - raise QuantumFunctionError( - "Adjoint differentiation method does not support expectation return type " - "mixed with other return types" - ) + # TODO: Investigate usefulness of creating numpy random generator + seed = np.random.randint(0, high=10000000) if seed == "global" else seed + self._rng = np.random.default_rng(seed) - for measurement in measurements: - if isinstance(measurement.obs, Tensor): - if any(isinstance(obs, Projector) for obs in measurement.obs.non_identity_obs): - raise QuantumFunctionError( - "Adjoint differentiation method does " - "not support the Projector observable" - ) - elif isinstance(measurement.obs, Projector): - raise QuantumFunctionError( - "Adjoint differentiation method does not support the Projector observable" - ) - return Expectation - - @staticmethod - def _check_adjdiff_supported_operations(operations): - """Check Lightning adjoint differentiation method support for a tape. - - Raise ``QuantumFunctionError`` if ``tape`` contains not supported measurements, - observables, or operations by the Lightning adjoint differentiation method. - - Args: - tape (.QuantumTape): quantum tape to differentiate. - """ - for operation in operations: - if operation.num_params > 1 and not isinstance(operation, Rot): - raise QuantumFunctionError( - f"The {operation.name} operation is not supported using " - 'the "adjoint" differentiation method' - ) - - def _init_process_jacobian_tape(self, tape, starting_state, use_device_state): - """Generate an initial state vector for ``_process_jacobian_tape``.""" - if starting_state is not None: - if starting_state.size != 2 ** len(self.wires): - raise QuantumFunctionError( - "The number of qubits of starting_state must be the same as " - "that of the device." - ) - self._apply_state_vector(starting_state, self.wires) - elif not use_device_state: - self.reset() - self.apply(tape.operations) - return self.state_vector - - def adjoint_jacobian(self, tape, starting_state=None, use_device_state=False): - """Computes and returns the Jacobian with the adjoint method.""" - if self.shots is not None: - warn( - "Requested adjoint differentiation to be computed with finite shots. " - "The derivative is always exact when using the adjoint " - "differentiation method.", - UserWarning, + self._c_dtype = c_dtype + self._batch_obs = batch_obs + self._mcmc = mcmc + if self._mcmc: + if kernel_name not in [ + "Local", + "NonZeroRandom", + ]: + raise NotImplementedError( + f"The {kernel_name} is not supported and currently " + "only 'Local' and 'NonZeroRandom' kernels are supported." ) + shots = shots if isinstance(shots, Sequence) else [shots] + if any(num_burnin >= s for s in shots): + raise ValueError("Shots should be greater than num_burnin.") + self._kernel_name = kernel_name + self._num_burnin = num_burnin + else: + self._kernel_name = None + self._num_burnin = None + + @property + def c_dtype(self): + """State vector complex data type.""" + return self._c_dtype + + dtype = c_dtype + + def _setup_execution_config(self, config): + """ + Update the execution config with choices for how the device should be used and the device options. + """ + updated_values = {} + if config.gradient_method == "best": + updated_values["gradient_method"] = "adjoint" + if config.use_device_gradient is None: + updated_values["use_device_gradient"] = config.gradient_method in ("best", "adjoint") + if config.grad_on_execution is None: + updated_values["grad_on_execution"] = True - tape_return_type = self._check_adjdiff_supported_measurements(tape.measurements) + new_device_options = dict(config.device_options) + for option in self._device_options: + if option not in new_device_options: + new_device_options[option] = getattr(self, f"_{option}", None) - if not tape_return_type: # the tape does not have measurements - return np.array([], dtype=self.state.dtype) + return replace(config, **updated_values, device_options=new_device_options) - if tape_return_type is State: - raise QuantumFunctionError( - "This method does not support statevector return type. " - "Use vjp method instead for this purpose." - ) + def preprocess(self, execution_config: ExecutionConfig = DefaultExecutionConfig): + """This function defines the device transform program to be applied and an updated device configuration. + + Args: + execution_config (Union[ExecutionConfig, Sequence[ExecutionConfig]]): A data structure describing the + parameters needed to fully describe the execution. + + Returns: + TransformProgram, ExecutionConfig: A transform program that when called returns :class:`~.QuantumTape`'s that the + device can natively execute as well as a postprocessing function to be called after execution, and a configuration + with unset specifications filled in. - self._check_adjdiff_supported_operations(tape.operations) + This device: - processed_data = self._process_jacobian_tape(tape, starting_state, use_device_state) + * Supports any qubit operations that provide a matrix + * Currently does not support finite shots + * Currently does not intrinsically support parameter broadcasting - if not processed_data: # training_params is empty - return np.array([], dtype=self.state.dtype) + """ + config = self._setup_execution_config(execution_config) + program = TransformProgram() + + program.add_transform(validate_measurements, name=self.name) + program.add_transform(validate_observables, accepted_observables, name=self.name) + program.add_transform(validate_device_wires, self.wires, name=self.name) + program.add_transform(qml.defer_measurements, device=self) + program.add_transform(decompose, stopping_condition=stopping_condition, name=self.name) + program.add_transform(qml.transforms.broadcast_expand) + + if config.gradient_method == "adjoint": + _add_adjoint_transforms(program) + return program, config + + # pylint: disable=unused-argument + def execute( + self, + circuits: QuantumTape_or_Batch, + execution_config: ExecutionConfig = DefaultExecutionConfig, + ) -> Result_or_ResultBatch: + """Execute a circuit or a batch of circuits and turn it into results. + + Args: + circuits (Union[QuantumTape, Sequence[QuantumTape]]): the quantum circuits to be executed + execution_config (ExecutionConfig): a datastructure with additional information required for execution - trainable_params = processed_data["tp_shift"] + Returns: + TensorLike, tuple[TensorLike], tuple[tuple[TensorLike]]: A numeric result of the computation. + """ + mcmc = { + "mcmc": self._mcmc, + "kernel_name": self._kernel_name, + "num_burnin": self._num_burnin, + } + results = [] + for circuit in circuits: + circuit = circuit.map_to_standard_wires() + results.append(simulate(circuit, self._statevector, mcmc=mcmc)) + + return tuple(results) + + def supports_derivatives( + self, + execution_config: Optional[ExecutionConfig] = None, + circuit: Optional[qml.tape.QuantumTape] = None, + ) -> bool: + """Check whether or not derivatives are available for a given configuration and circuit. + + ``LightningQubit`` supports adjoint differentiation with analytic results. - # If requested batching over observables, chunk into OMP_NUM_THREADS sized chunks. - # This will allow use of Lightning with adjoint for large-qubit numbers AND large - # numbers of observables, enabling choice between compute time and memory use. - requested_threads = int(getenv("OMP_NUM_THREADS", "1")) + Args: + execution_config (ExecutionConfig): The configuration of the desired derivative calculation + circuit (QuantumTape): An optional circuit to check derivatives support for. - adjoint_jacobian = AdjointJacobianC64() if self.use_csingle else AdjointJacobianC128() + Returns: + Bool: Whether or not a derivative can be calculated provided the given information - if self._batch_obs and requested_threads > 1: - obs_partitions = _chunk_iterable( - processed_data["obs_serialized"], requested_threads - ) - jac = [] - for obs_chunk in obs_partitions: - jac_local = adjoint_jacobian( - processed_data["state_vector"], - obs_chunk, - processed_data["ops_serialized"], - trainable_params, - ) - jac.extend(jac_local) - else: - jac = adjoint_jacobian( - processed_data["state_vector"], - processed_data["obs_serialized"], - processed_data["ops_serialized"], - trainable_params, - ) - jac = np.array(jac) - jac = jac.reshape(-1, len(trainable_params)) - jac_r = np.zeros((jac.shape[0], processed_data["all_params"])) - jac_r[:, processed_data["record_tp_rows"]] = jac - if hasattr(qml, "active_return"): # pragma: no cover - return self._adjoint_jacobian_processing(jac_r) if qml.active_return() else jac_r - return self._adjoint_jacobian_processing(jac_r) - - # pylint: disable=line-too-long, inconsistent-return-statements - def vjp(self, measurements, grad_vec, starting_state=None, use_device_state=False): - """Generate the processing function required to compute the vector-Jacobian products - of a tape. - - This function can be used with multiple expectation values or a quantum state. - When a quantum state is given, - - .. code-block:: python - - vjp_f = dev.vjp([qml.state()], grad_vec) - vjp = vjp_f(tape) - - computes :math:`w = (w_1,\\cdots,w_m)` where - - .. math:: - - w_k = \\langle v| \\frac{\\partial}{\\partial \\theta_k} | \\psi_{\\pmb{\\theta}} \\rangle. - - Here, :math:`m` is the total number of trainable parameters, :math:`\\pmb{\\theta}` - is the vector of trainable parameters and :math:`\\psi_{\\pmb{\\theta}}` - is the output quantum state. - - Args: - measurements (list): List of measurement processes for vector-Jacobian product. - Now it must be expectation values or a quantum state. - grad_vec (tensor_like): Gradient-output vector. Must have shape matching the output - shape of the corresponding tape, i.e. number of measurements if - the return type is expectation or :math:`2^N` if the return type is statevector - starting_state (tensor_like): post-forward pass state to start execution with. - It should be complex-valued. Takes precedence over ``use_device_state``. - use_device_state (bool): use current device state to initialize. - A forward pass of the same circuit should be the last thing - the device has executed. If a ``starting_state`` is provided, - that takes precedence. - - Returns: - The processing function required to compute the vector-Jacobian products of a tape. - """ - if self.shots is not None: - warn( - "Requested adjoint differentiation to be computed with finite shots. " - "The derivative is always exact when using the adjoint differentiation " - "method.", - UserWarning, - ) + """ + if execution_config is None and circuit is None: + return True + if execution_config.gradient_method not in {"adjoint", "best"}: + return False + if circuit is None: + return True + return _supports_adjoint(circuit=circuit) + + def compute_derivatives( + self, + circuits: QuantumTape_or_Batch, + execution_config: ExecutionConfig = DefaultExecutionConfig, + ): + """Calculate the jacobian of either a single or a batch of circuits on the device. - tape_return_type = self._check_adjdiff_supported_measurements(measurements) + Args: + circuits (Union[QuantumTape, Sequence[QuantumTape]]): the circuits to calculate derivatives for + execution_config (ExecutionConfig): a datastructure with all additional information required for execution - if math.allclose(grad_vec, 0) or tape_return_type is None: - return lambda tape: math.convert_like( - np.zeros(len(tape.trainable_params)), grad_vec - ) + Returns: + Tuple: The jacobian for each trainable parameter + """ + batch_obs = execution_config.device_options.get("batch_obs", self._batch_obs) + return tuple( + jacobian(circuit, self._statevector, batch_obs=batch_obs) for circuit in circuits + ) + + def execute_and_compute_derivatives( + self, + circuits: QuantumTape_or_Batch, + execution_config: ExecutionConfig = DefaultExecutionConfig, + ): + """Compute the results and jacobians of circuits at the same time. - if tape_return_type is Expectation: - if len(grad_vec) != len(measurements): - raise ValueError( - "Number of observables in the tape must be the same as the " - "length of grad_vec in the vjp method" - ) - - if np.iscomplexobj(grad_vec): - raise ValueError( - "The vjp method only works with a real-valued grad_vec when the " - "tape is returning an expectation value" - ) - - ham = qml.Hamiltonian(grad_vec, [m.obs for m in measurements]) - - def processing_fn_expval(tape): - nonlocal ham - num_params = len(tape.trainable_params) - - if num_params == 0: - return np.array([], dtype=self.state.dtype) - - new_tape = tape.copy() - new_tape._measurements = [qml.expval(ham)] - - return self.adjoint_jacobian(new_tape, starting_state, use_device_state) - - return processing_fn_expval - - if tape_return_type is State: - if len(grad_vec) != 2 ** len(self.wires): - raise ValueError( - "Size of the provided vector grad_vec must be the same as " - "the size of the statevector" - ) - if np.isrealobj(grad_vec): - warn( - "The vjp method only works with complex-valued grad_vec when " - "the tape is returning a statevector. Upcasting grad_vec." - ) - - grad_vec = grad_vec.astype(self.C_DTYPE) - - def processing_fn_state(tape): - nonlocal grad_vec - processed_data = self._process_jacobian_tape( - tape, starting_state, use_device_state - ) - calculate_vjp = ( - VectorJacobianProductC64() - if self.use_csingle - else VectorJacobianProductC128() - ) - - return calculate_vjp( - processed_data["state_vector"], - processed_data["ops_serialized"], - grad_vec, - processed_data["tp_shift"], - ) - - return processing_fn_state - -else: - - class LightningQubit(LightningBaseFallBack): # pragma: no cover - # pylint: disable=missing-class-docstring, too-few-public-methods - name = "Lightning qubit PennyLane plugin [No binaries found - Fallback: default.qubit]" - short_name = "lightning.qubit" - - def __init__(self, wires, *, c_dtype=np.complex128, **kwargs): - warn( - "Pre-compiled binaries for lightning.qubit are not available. Falling back to " - "using the Python-based default.qubit implementation. To manually compile from " - "source, follow the instructions at " - "https://pennylane-lightning.readthedocs.io/en/latest/installation.html.", - UserWarning, - ) - super().__init__(wires, c_dtype=c_dtype, **kwargs) + Args: + circuits (Union[QuantumTape, Sequence[QuantumTape]]): the circuits or batch of circuits + execution_config (ExecutionConfig): a datastructure with all additional information required for execution + + Returns: + tuple: A numeric result of the computation and the gradient. + """ + batch_obs = execution_config.device_options.get("batch_obs", self._batch_obs) + results = tuple( + simulate_and_jacobian(c, self._statevector, batch_obs=batch_obs) for c in circuits + ) + return tuple(zip(*results)) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit2.py b/pennylane_lightning/lightning_qubit/lightning_qubit2.py deleted file mode 100644 index 4a0d8b6200..0000000000 --- a/pennylane_lightning/lightning_qubit/lightning_qubit2.py +++ /dev/null @@ -1,527 +0,0 @@ -# Copyright 2018-2024 Xanadu Quantum Technologies Inc. - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -""" -This module contains the LightningQubit2 class that inherits from the new device interface. -""" -from dataclasses import replace -from pathlib import Path -from typing import Callable, Optional, Sequence, Union - -import numpy as np -import pennylane as qml -from pennylane.devices import DefaultExecutionConfig, Device, ExecutionConfig -from pennylane.devices.default_qubit import adjoint_ops -from pennylane.devices.modifiers import simulator_tracking, single_tape_support -from pennylane.devices.preprocess import ( - decompose, - no_sampling, - validate_adjoint_trainable_params, - validate_device_wires, - validate_measurements, - validate_observables, -) -from pennylane.tape import QuantumScript, QuantumTape -from pennylane.transforms.core import TransformProgram -from pennylane.typing import Result, ResultBatch - -from ._adjoint_jacobian import LightningAdjointJacobian -from ._measurements import LightningMeasurements -from ._state_vector import LightningStateVector - -try: - # pylint: disable=import-error, unused-import - from pennylane_lightning.lightning_qubit_ops import backend_info - - LQ_CPP_BINARY_AVAILABLE = True -except ImportError: - LQ_CPP_BINARY_AVAILABLE = False - -Result_or_ResultBatch = Union[Result, ResultBatch] -QuantumTapeBatch = Sequence[QuantumTape] -QuantumTape_or_Batch = Union[QuantumTape, QuantumTapeBatch] -PostprocessingFn = Callable[[ResultBatch], Result_or_ResultBatch] - - -def simulate(circuit: QuantumScript, state: LightningStateVector, mcmc: dict = None) -> Result: - """Simulate a single quantum script. - - Args: - circuit (QuantumTape): The single circuit to simulate - state (LightningStateVector): handle to Lightning state vector - mcmc (dict): Dictionary containing the Markov Chain Monte Carlo - parameters: mcmc, kernel_name, num_burnin. Descriptions of - these fields are found in :class:`~.LightningQubit2`. - - Returns: - Tuple[TensorLike]: The results of the simulation - - Note that this function can return measurements for non-commuting observables simultaneously. - """ - if mcmc is None: - mcmc = {} - state.reset_state() - final_state = state.get_final_state(circuit) - return LightningMeasurements(final_state, **mcmc).measure_final_state(circuit) - - -def jacobian(circuit: QuantumTape, state: LightningStateVector, batch_obs=False): - """Compute the Jacobian for a single quantum script. - - Args: - circuit (QuantumTape): The single circuit to simulate - state (LightningStateVector): handle to Lightning state vector - batch_obs (bool): Determine whether we process observables in parallel when - computing the jacobian. This value is only relevant when the lightning - qubit is built with OpenMP. Default is False. - - Returns: - TensorLike: The Jacobian of the quantum script - """ - circuit = circuit.map_to_standard_wires() - state.reset_state() - final_state = state.get_final_state(circuit) - return LightningAdjointJacobian(final_state, batch_obs=batch_obs).calculate_jacobian(circuit) - - -def simulate_and_jacobian(circuit: QuantumTape, state: LightningStateVector, batch_obs=False): - """Simulate a single quantum script and compute its Jacobian. - - Args: - circuit (QuantumTape): The single circuit to simulate - state (LightningStateVector): handle to Lightning state vector - batch_obs (bool): Determine whether we process observables in parallel when - computing the jacobian. This value is only relevant when the lightning - qubit is built with OpenMP. Default is False. - - Returns: - Tuple[TensorLike]: The results of the simulation and the calculated Jacobian - - Note that this function can return measurements for non-commuting observables simultaneously. - """ - circuit = circuit.map_to_standard_wires() - res = simulate(circuit, state) - jac = LightningAdjointJacobian(state, batch_obs=batch_obs).calculate_jacobian(circuit) - return res, jac - - -_operations = frozenset( - { - "Identity", - "BasisState", - "QubitStateVector", - "StatePrep", - "QubitUnitary", - "ControlledQubitUnitary", - "MultiControlledX", - "DiagonalQubitUnitary", - "PauliX", - "PauliY", - "PauliZ", - "MultiRZ", - "GlobalPhase", - "Hadamard", - "S", - "Adjoint(S)", - "T", - "Adjoint(T)", - "SX", - "Adjoint(SX)", - "CNOT", - "SWAP", - "ISWAP", - "PSWAP", - "Adjoint(ISWAP)", - "SISWAP", - "Adjoint(SISWAP)", - "SQISW", - "CSWAP", - "Toffoli", - "CY", - "CZ", - "PhaseShift", - "ControlledPhaseShift", - "CPhase", - "RX", - "RY", - "RZ", - "Rot", - "CRX", - "CRY", - "CRZ", - "C(PauliX)", - "C(PauliY)", - "C(PauliZ)", - "C(Hadamard)", - "C(S)", - "C(T)", - "C(PhaseShift)", - "C(RX)", - "C(RY)", - "C(RZ)", - "C(Rot)", - "C(SWAP)", - "C(IsingXX)", - "C(IsingXY)", - "C(IsingYY)", - "C(IsingZZ)", - "C(SingleExcitation)", - "C(SingleExcitationMinus)", - "C(SingleExcitationPlus)", - "C(DoubleExcitation)", - "C(DoubleExcitationMinus)", - "C(DoubleExcitationPlus)", - "C(MultiRZ)", - "C(GlobalPhase)", - "CRot", - "IsingXX", - "IsingYY", - "IsingZZ", - "IsingXY", - "SingleExcitation", - "SingleExcitationPlus", - "SingleExcitationMinus", - "DoubleExcitation", - "DoubleExcitationPlus", - "DoubleExcitationMinus", - "QubitCarry", - "QubitSum", - "OrbitalRotation", - "QFT", - "ECR", - "BlockEncode", - } -) -# The set of supported operations. - - -_observables = frozenset( - { - "PauliX", - "PauliY", - "PauliZ", - "Hadamard", - "Hermitian", - "Identity", - "Projector", - "SparseHamiltonian", - "Hamiltonian", - "Sum", - "SProd", - "Prod", - "Exp", - } -) -# The set of supported observables. - - -def stopping_condition(op: qml.operation.Operator) -> bool: - """A function that determines whether or not an operation is supported by ``lightning.qubit``.""" - return op.name in _operations - - -def accepted_observables(obs: qml.operation.Operator) -> bool: - """A function that determines whether or not an observable is supported by ``lightning.qubit``.""" - return obs.name in _observables - - -def adjoint_measurements(mp: qml.measurements.MeasurementProcess) -> bool: - """Specifies whether or not an observable is compatible with adjoint differentiation on DefaultQubit.""" - return isinstance(mp, qml.measurements.ExpectationMP) - - -def _supports_adjoint(circuit): - if circuit is None: - return True - - prog = TransformProgram() - _add_adjoint_transforms(prog) - - try: - prog((circuit,)) - except (qml.operation.DecompositionUndefinedError, qml.DeviceError): - return False - return True - - -def _add_adjoint_transforms(program: TransformProgram) -> None: - """Private helper function for ``preprocess`` that adds the transforms specific - for adjoint differentiation. - - Args: - program (TransformProgram): where we will add the adjoint differentiation transforms - - Side Effects: - Adds transforms to the input program. - - """ - - name = "adjoint + lightning.qubit" - program.add_transform(no_sampling, name=name) - program.add_transform(decompose, stopping_condition=adjoint_ops, name=name) - program.add_transform(validate_observables, accepted_observables, name=name) - program.add_transform( - validate_measurements, analytic_measurements=adjoint_measurements, name=name - ) - program.add_transform(qml.transforms.broadcast_expand) - program.add_transform(validate_adjoint_trainable_params) - - -@simulator_tracking -@single_tape_support -class LightningQubit2(Device): - """PennyLane Lightning Qubit device. - - A device that interfaces with C++ to perform fast linear algebra calculations. - - Use of this device requires pre-built binaries or compilation from source. Check out the - :doc:`/lightning_qubit/installation` guide for more details. - - Args: - wires (int): the number of wires to initialize the device with - c_dtype: Datatypes for statevector representation. Must be one of - ``np.complex64`` or ``np.complex128``. - shots (int): How many times the circuit should be evaluated (or sampled) to estimate - the expectation values. Defaults to ``None`` if not specified. Setting - to ``None`` results in computing statistics like expectation values and - variances analytically. - seed (Union[str, None, int, array_like[int], SeedSequence, BitGenerator, Generator]): A - seed-like parameter matching that of ``seed`` for ``numpy.random.default_rng``, or - a request to seed from numpy's global random number generator. - The default, ``seed="global"`` pulls a seed from NumPy's global generator. ``seed=None`` - will pull a seed from the OS entropy. - mcmc (bool): Determine whether to use the approximate Markov Chain Monte Carlo - sampling method when generating samples. - kernel_name (str): name of transition MCMC kernel. The current version supports - two kernels: ``"Local"`` and ``"NonZeroRandom"``. - The local kernel conducts a bit-flip local transition between states. - The local kernel generates a random qubit site and then generates a random - number to determine the new bit at that qubit site. The ``"NonZeroRandom"`` kernel - randomly transits between states that have nonzero probability. - num_burnin (int): number of MCMC steps that will be dropped. Increasing this value will - result in a closer approximation but increased runtime. - batch_obs (bool): Determine whether we process observables in parallel when - computing the jacobian. This value is only relevant when the lightning - qubit is built with OpenMP. - """ - - _device_options = ("rng", "c_dtype", "batch_obs", "mcmc", "kernel_name", "num_burnin") - _CPP_BINARY_AVAILABLE = LQ_CPP_BINARY_AVAILABLE - _new_API = True - _backend_info = backend_info if LQ_CPP_BINARY_AVAILABLE else None - _config = Path(__file__).parent / "lightning_qubit.toml" - - # TODO: Move supported ops/obs to TOML file - operations = _operations - # The names of the supported operations. - - observables = _observables - # The names of the supported observables. - - def __init__( # pylint: disable=too-many-arguments - self, - wires, - *, - c_dtype=np.complex128, - shots=None, - seed="global", - mcmc=False, - kernel_name="Local", - num_burnin=100, - batch_obs=False, - ): - if not self._CPP_BINARY_AVAILABLE: - raise ImportError( - "Pre-compiled binaries for lightning.qubit are not available. " - "To manually compile from source, follow the instructions at " - "https://pennylane-lightning.readthedocs.io/en/latest/installation.html." - ) - - super().__init__(wires=wires, shots=shots) - - self._statevector = LightningStateVector(num_wires=len(self.wires), dtype=c_dtype) - - # TODO: Investigate usefulness of creating numpy random generator - seed = np.random.randint(0, high=10000000) if seed == "global" else seed - self._rng = np.random.default_rng(seed) - - self._c_dtype = c_dtype - self._batch_obs = batch_obs - self._mcmc = mcmc - if self._mcmc: - if kernel_name not in [ - "Local", - "NonZeroRandom", - ]: - raise NotImplementedError( - f"The {kernel_name} is not supported and currently " - "only 'Local' and 'NonZeroRandom' kernels are supported." - ) - shots = shots if isinstance(shots, Sequence) else [shots] - if any(num_burnin >= s for s in shots): - raise ValueError("Shots should be greater than num_burnin.") - self._kernel_name = kernel_name - self._num_burnin = num_burnin - else: - self._kernel_name = None - self._num_burnin = None - - @property - def c_dtype(self): - """State vector complex data type.""" - return self._c_dtype - - dtype = c_dtype - - def _setup_execution_config(self, config): - """ - Update the execution config with choices for how the device should be used and the device options. - """ - updated_values = {} - if config.gradient_method == "best": - updated_values["gradient_method"] = "adjoint" - if config.use_device_gradient is None: - updated_values["use_device_gradient"] = config.gradient_method in ("best", "adjoint") - if config.grad_on_execution is None: - updated_values["grad_on_execution"] = True - - new_device_options = dict(config.device_options) - for option in self._device_options: - if option not in new_device_options: - new_device_options[option] = getattr(self, f"_{option}", None) - - return replace(config, **updated_values, device_options=new_device_options) - - def preprocess(self, execution_config: ExecutionConfig = DefaultExecutionConfig): - """This function defines the device transform program to be applied and an updated device configuration. - - Args: - execution_config (Union[ExecutionConfig, Sequence[ExecutionConfig]]): A data structure describing the - parameters needed to fully describe the execution. - - Returns: - TransformProgram, ExecutionConfig: A transform program that when called returns :class:`~.QuantumTape`'s that the - device can natively execute as well as a postprocessing function to be called after execution, and a configuration - with unset specifications filled in. - - This device: - - * Supports any qubit operations that provide a matrix - * Currently does not support finite shots - * Currently does not intrinsically support parameter broadcasting - - """ - config = self._setup_execution_config(execution_config) - program = TransformProgram() - - program.add_transform(validate_measurements, name=self.name) - program.add_transform(validate_observables, accepted_observables, name=self.name) - program.add_transform(validate_device_wires, self.wires, name=self.name) - program.add_transform(qml.defer_measurements, device=self) - program.add_transform(decompose, stopping_condition=stopping_condition, name=self.name) - program.add_transform(qml.transforms.broadcast_expand) - - if config.gradient_method == "adjoint": - _add_adjoint_transforms(program) - return program, config - - # pylint: disable=unused-argument - def execute( - self, - circuits: QuantumTape_or_Batch, - execution_config: ExecutionConfig = DefaultExecutionConfig, - ) -> Result_or_ResultBatch: - """Execute a circuit or a batch of circuits and turn it into results. - - Args: - circuits (Union[QuantumTape, Sequence[QuantumTape]]): the quantum circuits to be executed - execution_config (ExecutionConfig): a datastructure with additional information required for execution - - Returns: - TensorLike, tuple[TensorLike], tuple[tuple[TensorLike]]: A numeric result of the computation. - """ - mcmc = { - "mcmc": self._mcmc, - "kernel_name": self._kernel_name, - "num_burnin": self._num_burnin, - } - results = [] - for circuit in circuits: - circuit = circuit.map_to_standard_wires() - results.append(simulate(circuit, self._statevector, mcmc=mcmc)) - - return tuple(results) - - def supports_derivatives( - self, - execution_config: Optional[ExecutionConfig] = None, - circuit: Optional[qml.tape.QuantumTape] = None, - ) -> bool: - """Check whether or not derivatives are available for a given configuration and circuit. - - ``LightningQubit2`` supports adjoint differentiation with analytic results. - - Args: - execution_config (ExecutionConfig): The configuration of the desired derivative calculation - circuit (QuantumTape): An optional circuit to check derivatives support for. - - Returns: - Bool: Whether or not a derivative can be calculated provided the given information - - """ - if execution_config is None and circuit is None: - return True - if execution_config.gradient_method not in {"adjoint", "best"}: - return False - if circuit is None: - return True - return _supports_adjoint(circuit=circuit) - - def compute_derivatives( - self, - circuits: QuantumTape_or_Batch, - execution_config: ExecutionConfig = DefaultExecutionConfig, - ): - """Calculate the jacobian of either a single or a batch of circuits on the device. - - Args: - circuits (Union[QuantumTape, Sequence[QuantumTape]]): the circuits to calculate derivatives for - execution_config (ExecutionConfig): a datastructure with all additional information required for execution - - Returns: - Tuple: The jacobian for each trainable parameter - """ - batch_obs = execution_config.device_options.get("batch_obs", self._batch_obs) - return tuple( - jacobian(circuit, self._statevector, batch_obs=batch_obs) for circuit in circuits - ) - - def execute_and_compute_derivatives( - self, - circuits: QuantumTape_or_Batch, - execution_config: ExecutionConfig = DefaultExecutionConfig, - ): - """Compute the results and jacobians of circuits at the same time. - - Args: - circuits (Union[QuantumTape, Sequence[QuantumTape]]): the circuits or batch of circuits - execution_config (ExecutionConfig): a datastructure with all additional information required for execution - - Returns: - tuple: A numeric result of the computation and the gradient. - """ - batch_obs = execution_config.device_options.get("batch_obs", self._batch_obs) - results = tuple( - simulate_and_jacobian(c, self._statevector, batch_obs=batch_obs) for c in circuits - ) - return tuple(zip(*results)) From d557b651f4841bec2a25046f9b6adbe5252e23de Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Tue, 19 Mar 2024 11:15:21 -0400 Subject: [PATCH 221/428] add device name (necessary for pl-device-test) --- pennylane_lightning/lightning_qubit/lightning_qubit.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit.py b/pennylane_lightning/lightning_qubit/lightning_qubit.py index 200c7584f7..5f42947376 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit.py @@ -376,6 +376,11 @@ def __init__( # pylint: disable=too-many-arguments self._kernel_name = None self._num_burnin = None + @property + def name(self): + """The name of the device.""" + return "lightning.qubit" + @property def c_dtype(self): """State vector complex data type.""" From 472e0ff5bce8e4a86cfd4a0b6df43097c384aca0 Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Tue, 19 Mar 2024 15:38:03 +0000 Subject: [PATCH 222/428] Add _measure_hamiltonian_with_samples _measure_sum_with_samples --- .../lightning_qubit/_measurements.py | 59 ++++++++++++++++--- .../test_measurements_class.py | 16 +++-- 2 files changed, 62 insertions(+), 13 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/_measurements.py b/pennylane_lightning/lightning_qubit/_measurements.py index aae3d8a20c..e26e8fceec 100644 --- a/pennylane_lightning/lightning_qubit/_measurements.py +++ b/pennylane_lightning/lightning_qubit/_measurements.py @@ -320,18 +320,21 @@ def measure_with_samples( if isinstance(group[0], (ExpectationMP, VarianceMP)) and isinstance( group[0].obs, SparseHamiltonian ): - raise TypeError("ExpectationMP(SparseHamiltonian) cannot be computed with samples.") - if isinstance(group[0], (ExpectationMP, VarianceMP)) and isinstance( - group[0].obs, Hamiltonian - ): - raise TypeError("ExpectationMP(Hamiltonian) cannot be computed with samples.") - if isinstance(group[0], (ExpectationMP, VarianceMP)) and isinstance(group[0].obs, Sum): - raise TypeError("ExpectationMP(Sum) cannot be computed with samples.") + raise TypeError( + "ExpectationMP/VarianceMP(SparseHamiltonian) cannot be computed with samples." + ) + if isinstance(group[0], VarianceMP) and isinstance(group[0].obs, (Hamiltonian, Sum)): + raise TypeError("VarianceMP(Hamiltonian/Sum) cannot be computed with samples.") if isinstance(group[0], (ClassicalShadowMP, ShadowExpvalMP)): raise TypeError( "ExpectationMP(ClassicalShadowMP, ShadowExpvalMP) cannot be computed with samples." ) - all_res.extend(self._measure_with_samples_diagonalizing_gates(group, shots)) + if isinstance(group[0], ExpectationMP) and isinstance(group[0].obs, Hamiltonian): + all_res.extend(self._measure_hamiltonian_with_samples(group, shots)) + elif isinstance(group[0], ExpectationMP) and isinstance(group[0].obs, Sum): + all_res.extend(self._measure_sum_with_samples(group, shots)) + else: + all_res.extend(self._measure_with_samples_diagonalizing_gates(group, shots)) # reorder results flat_indices = [] @@ -438,3 +441,43 @@ def _process_single_shot(samples): self._apply_diagonalizing_gates(mps, adjoint=True) return _process_single_shot(samples) + + def _measure_hamiltonian_with_samples( + self, + mp: List[SampleMeasurement], + shots: Shots, + ): + # the list contains only one element based on how we group measurements + mp = mp[0] + + # if the measurement process involves a Hamiltonian, measure each + # of the terms separately and sum + def _sum_for_single_shot(s): + results = self.measure_with_samples( + [ExpectationMP(t) for t in mp.obs.terms()[1]], + s, + ) + return sum(c * res for c, res in zip(mp.obs.terms()[0], results)) + + unsqueezed_results = tuple(_sum_for_single_shot(type(shots)(s)) for s in shots) + return [unsqueezed_results] if shots.has_partitioned_shots else [unsqueezed_results[0]] + + def _measure_sum_with_samples( + self, + mp: List[SampleMeasurement], + shots: Shots, + ): + # the list contains only one element based on how we group measurements + mp = mp[0] + + # if the measurement process involves a Sum, measure each + # of the terms separately and sum + def _sum_for_single_shot(s): + results = self.measure_with_samples( + [ExpectationMP(t) for t in mp.obs], + s, + ) + return sum(results) + + unsqueezed_results = tuple(_sum_for_single_shot(type(shots)(s)) for s in shots) + return [unsqueezed_results] if shots.has_partitioned_shots else [unsqueezed_results[0]] diff --git a/tests/lightning_qubit/test_measurements_class.py b/tests/lightning_qubit/test_measurements_class.py index 6717dec859..1f0337cd6e 100644 --- a/tests/lightning_qubit/test_measurements_class.py +++ b/tests/lightning_qubit/test_measurements_class.py @@ -548,11 +548,17 @@ def test_double_return_value(self, shots, measurement, obs0_, obs1_, lightning_s qml.Hamiltonian, qml.SparseHamiltonian, ) - if ( - (measurement is qml.expval or measurement is qml.var) - and shots is not None - and (isinstance(obs0_, skip_list) or isinstance(obs1_, skip_list)) - ): + do_skip = measurement is qml.var and ( + isinstance(obs0_, skip_list) or isinstance(obs1_, skip_list) + ) + do_skip = do_skip or ( + measurement is qml.expval + and ( + isinstance(obs0_, qml.SparseHamiltonian) or isinstance(obs1_, qml.SparseHamiltonian) + ) + ) + do_skip = do_skip and shots is not None + if do_skip: with pytest.raises(TypeError): _ = m.measure_final_state(tape) return From f1b0a62c6c21da2cf9c4620b1c17d5fab946ff89 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Tue, 19 Mar 2024 11:44:50 -0400 Subject: [PATCH 223/428] fix tests without binary --- .github/workflows/tests_without_binary.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/tests_without_binary.yml b/.github/workflows/tests_without_binary.yml index febea38be3..680b2b75c3 100644 --- a/.github/workflows/tests_without_binary.yml +++ b/.github/workflows/tests_without_binary.yml @@ -107,7 +107,6 @@ jobs: cd main/ DEVICENAME=`echo ${{ matrix.pl_backend }} | sed "s/_/./g"` PL_DEVICE=${DEVICENAME} python -m pytest tests/ $COVERAGE_FLAGS - if [ ${{ matrix.pl_backend }} == "lightning_qubit" ]; then PL_DEVICE=lightning_qubit2 python -m pytest tests/ $COVERAGE_FLAGS --cov-append; fi pl-device-test --device ${DEVICENAME} --skip-ops --shots=20000 $COVERAGE_FLAGS --cov-append pl-device-test --device ${DEVICENAME} --shots=None --skip-ops $COVERAGE_FLAGS --cov-append From 1803e3996aede58edce80f09279da6771f11ead9 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Tue, 19 Mar 2024 11:57:32 -0400 Subject: [PATCH 224/428] check for jac size before reshaping --- pennylane_lightning/lightning_qubit/_adjoint_jacobian.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/lightning_qubit/_adjoint_jacobian.py b/pennylane_lightning/lightning_qubit/_adjoint_jacobian.py index ae45a550e6..3313fc9b62 100644 --- a/pennylane_lightning/lightning_qubit/_adjoint_jacobian.py +++ b/pennylane_lightning/lightning_qubit/_adjoint_jacobian.py @@ -243,7 +243,7 @@ def calculate_jacobian(self, tape: QuantumTape): trainable_params, ) jac = np.array(jac) - jac = jac.reshape(-1, len(trainable_params)) + jac = jac.reshape(-1, len(trainable_params)) if len(jac) else jac jac_r = np.zeros((jac.shape[0], processed_data["all_params"])) jac_r[:, processed_data["record_tp_rows"]] = jac From a4c73cadb2645e0a8bc02fceb48a8f3ba9f14fb4 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Tue, 19 Mar 2024 13:27:49 -0400 Subject: [PATCH 225/428] remove obsolete tests --- .github/workflows/tests_without_binary.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/tests_without_binary.yml b/.github/workflows/tests_without_binary.yml index 680b2b75c3..e01964ce2b 100644 --- a/.github/workflows/tests_without_binary.yml +++ b/.github/workflows/tests_without_binary.yml @@ -107,8 +107,6 @@ jobs: cd main/ DEVICENAME=`echo ${{ matrix.pl_backend }} | sed "s/_/./g"` PL_DEVICE=${DEVICENAME} python -m pytest tests/ $COVERAGE_FLAGS - pl-device-test --device ${DEVICENAME} --skip-ops --shots=20000 $COVERAGE_FLAGS --cov-append - pl-device-test --device ${DEVICENAME} --shots=None --skip-ops $COVERAGE_FLAGS --cov-append - name: Upload coverage to Codecov uses: codecov/codecov-action@v3 From e359c30bc9de915eef9fdc2a211ae8b0390f5d4f Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Tue, 19 Mar 2024 13:50:05 -0400 Subject: [PATCH 226/428] organize tests --- tests/lightning_qubit/__init__.py | 0 tests/new_api/__init__.py | 0 .../test_new_api_device.py => new_api/test_device.py} | 0 .../{lightning_qubit2/test_expval_2.py => new_api/test_expval.py} | 0 tests/{lightning_qubit2 => new_api}/test_no_binaries.py | 0 tests/{lightning_qubit2/test_var_2.py => new_api/test_var.py} | 0 6 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/lightning_qubit/__init__.py create mode 100644 tests/new_api/__init__.py rename tests/{lightning_qubit2/test_new_api_device.py => new_api/test_device.py} (100%) rename tests/{lightning_qubit2/test_expval_2.py => new_api/test_expval.py} (100%) rename tests/{lightning_qubit2 => new_api}/test_no_binaries.py (100%) rename tests/{lightning_qubit2/test_var_2.py => new_api/test_var.py} (100%) diff --git a/tests/lightning_qubit/__init__.py b/tests/lightning_qubit/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/new_api/__init__.py b/tests/new_api/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/lightning_qubit2/test_new_api_device.py b/tests/new_api/test_device.py similarity index 100% rename from tests/lightning_qubit2/test_new_api_device.py rename to tests/new_api/test_device.py diff --git a/tests/lightning_qubit2/test_expval_2.py b/tests/new_api/test_expval.py similarity index 100% rename from tests/lightning_qubit2/test_expval_2.py rename to tests/new_api/test_expval.py diff --git a/tests/lightning_qubit2/test_no_binaries.py b/tests/new_api/test_no_binaries.py similarity index 100% rename from tests/lightning_qubit2/test_no_binaries.py rename to tests/new_api/test_no_binaries.py diff --git a/tests/lightning_qubit2/test_var_2.py b/tests/new_api/test_var.py similarity index 100% rename from tests/lightning_qubit2/test_var_2.py rename to tests/new_api/test_var.py From 739f0b4c94446531ee804fc52db0593f9539be2e Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Tue, 19 Mar 2024 15:06:34 -0400 Subject: [PATCH 227/428] fix test for Windows wheels --- .github/workflows/wheel_win_x86_64.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/wheel_win_x86_64.yml b/.github/workflows/wheel_win_x86_64.yml index 7ba0704c0b..912d00a9bd 100644 --- a/.github/workflows/wheel_win_x86_64.yml +++ b/.github/workflows/wheel_win_x86_64.yml @@ -130,7 +130,8 @@ jobs: python -m pip install -r requirements-tests.txt CIBW_TEST_COMMAND: | - pl-device-test --device=lightning.qubit --skip-ops -x --tb=short --no-flaky-report + DEVICENAME=`echo ${{ matrix.pl_backend }} | sed "s/_/./g"` + pl-device-test --device=${DEVICENAME} --skip-ops -x --tb=short --no-flaky-report CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014 From 8c153b352e9a692322cdd1fb30e4d2dba342c8ac Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Tue, 19 Mar 2024 15:31:34 -0400 Subject: [PATCH 228/428] Adding LC to supported obs list for all devices --- pennylane_lightning/lightning_gpu/lightning_gpu.py | 1 + pennylane_lightning/lightning_kokkos/lightning_kokkos.py | 1 + pennylane_lightning/lightning_qubit/lightning_qubit.py | 1 + requirements-dev.txt | 2 +- 4 files changed, 4 insertions(+), 1 deletion(-) diff --git a/pennylane_lightning/lightning_gpu/lightning_gpu.py b/pennylane_lightning/lightning_gpu/lightning_gpu.py index cbc4a48340..a23bd085f4 100644 --- a/pennylane_lightning/lightning_gpu/lightning_gpu.py +++ b/pennylane_lightning/lightning_gpu/lightning_gpu.py @@ -199,6 +199,7 @@ def _mebibytesToBytes(mebibytes): "Hadamard", "SparseHamiltonian", "Hamiltonian", + "LinearCombination", "Hermitian", "Identity", "Sum", diff --git a/pennylane_lightning/lightning_kokkos/lightning_kokkos.py b/pennylane_lightning/lightning_kokkos/lightning_kokkos.py index d7821660bf..a61566a4c0 100644 --- a/pennylane_lightning/lightning_kokkos/lightning_kokkos.py +++ b/pennylane_lightning/lightning_kokkos/lightning_kokkos.py @@ -160,6 +160,7 @@ def _kokkos_configuration(): "Projector", "SparseHamiltonian", "Hamiltonian", + "LinearCombination", "Sum", "SProd", "Prod", diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit.py b/pennylane_lightning/lightning_qubit/lightning_qubit.py index 5f42947376..48ba784a2a 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit.py @@ -216,6 +216,7 @@ def simulate_and_jacobian(circuit: QuantumTape, state: LightningStateVector, bat "Projector", "SparseHamiltonian", "Hamiltonian", + "LinearCombination", "Sum", "SProd", "Prod", diff --git a/requirements-dev.txt b/requirements-dev.txt index e9467a9e54..801df5fc71 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,5 +1,5 @@ pip~=22.0 -git+https://github.com/PennyLaneAI/pennylane.git@master +git+https://github.com/PennyLaneAI/pennylane.git@getattr-ham-lc-aliasing ninja flaky pybind11 From 353e6bce0846c0a58e9e5ddf2abf67e9cec8061e Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Tue, 19 Mar 2024 17:03:36 -0400 Subject: [PATCH 229/428] Updating serialization --- pennylane_lightning/core/_serialize.py | 22 ++++++++++---------- tests/conftest.py | 28 ++++++++++++++++++++++++++ tests/test_serialize.py | 1 + 3 files changed, 40 insertions(+), 11 deletions(-) diff --git a/pennylane_lightning/core/_serialize.py b/pennylane_lightning/core/_serialize.py index fb9ff6dd82..8a81a7bc65 100644 --- a/pennylane_lightning/core/_serialize.py +++ b/pennylane_lightning/core/_serialize.py @@ -35,6 +35,7 @@ from pennylane.math import unwrap from pennylane.operation import Tensor from pennylane.tape import QuantumTape +from pennylane.ops import Prod pauli_name_map = { "I": "Identity", @@ -190,8 +191,9 @@ def _hermitian_ob(self, observable, wires_map: dict = None): def _tensor_ob(self, observable, wires_map: dict = None): """Serialize a tensor observable""" - assert isinstance(observable, Tensor) - return self.tensor_obs([self._ob(obs, wires_map) for obs in observable.obs]) + assert isinstance(observable, (Tensor, Prod)) + obs = observable.obs if isinstance(observable, Tensor) else observable.operands + return self.tensor_obs([self._ob(o, wires_map) for o in obs]) def _hamiltonian(self, observable, wires_map: dict = None): coeffs = np.array(unwrap(observable.coeffs)).astype(self.rtype) @@ -261,16 +263,16 @@ def _pauli_sentence(self, observable, wires_map: dict = None): # pylint: disable=protected-access def _ob(self, observable, wires_map: dict = None): """Serialize a :class:`pennylane.operation.Observable` into an Observable.""" - if isinstance(observable, Tensor): - return self._tensor_ob(observable, wires_map) - if observable.name == "Hamiltonian": - return self._hamiltonian(observable, wires_map) - if observable.name == "SparseHamiltonian": - return self._sparse_hamiltonian(observable, wires_map) if isinstance(observable, (PauliX, PauliY, PauliZ, Identity, Hadamard)): return self._named_obs(observable, wires_map) if observable._pauli_rep is not None: return self._pauli_sentence(observable._pauli_rep, wires_map) + if isinstance(observable, (Tensor, Prod)): + return self._tensor_ob(observable, wires_map) + if observable.name in ("Hamiltonian", "LinearCombination"): + return self._hamiltonian(observable, wires_map) + if observable.name == "SparseHamiltonian": + return self._sparse_hamiltonian(observable, wires_map) return self._hermitian_ob(observable, wires_map) def serialize_observables(self, tape: QuantumTape, wires_map: dict = None) -> List: @@ -298,9 +300,7 @@ def serialize_observables(self, tape: QuantumTape, wires_map: dict = None) -> Li offset_indices.append(offset_indices[-1] + 1) return serialized_obs, offset_indices - def serialize_ops( - self, tape: QuantumTape, wires_map: dict = None - ) -> Tuple[ + def serialize_ops(self, tape: QuantumTape, wires_map: dict = None) -> Tuple[ List[List[str]], List[np.ndarray], List[List[int]], diff --git a/tests/conftest.py b/tests/conftest.py index 1cc9480cb4..dce7468008 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -152,6 +152,34 @@ def _device(wires, shots=None): return _device +####################################################################### +# Fixtures for testing under new and old opmath + + +@pytest.fixture(scope="function") +def use_legacy_opmath(): + with qml.operation.disable_new_opmath_cm() as cm: + yield cm + + +@pytest.fixture(scope="function") +def use_new_opmath(): + with qml.operation.enable_new_opmath_cm() as cm: + yield cm + + +@pytest.fixture( + params=[qml.operation.disable_new_opmath_cm, qml.operation.enable_new_opmath_cm], + scope="function", +) +def use_legacy_and_new_opmath(request): + with request.param() as cm: + yield cm + + +####################################################################### + + def validate_counts(shots, results1, results2): """Compares two counts. diff --git a/tests/test_serialize.py b/tests/test_serialize.py index 1f726a55ef..dd5a8386d4 100644 --- a/tests/test_serialize.py +++ b/tests/test_serialize.py @@ -112,6 +112,7 @@ def test_obs_returns_expected_type(obs, obs_type): assert isinstance( QuantumScriptSerializer(device_name)._ob(obs, dict(enumerate(obs.wires))), obs_type ) + print(type(QuantumScriptSerializer(device_name)._ob(obs))) assert isinstance(QuantumScriptSerializer(device_name)._ob(obs), obs_type) From 6687fc8612d5db7257995a7fbb1fe9f7dbc79282 Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Wed, 20 Mar 2024 13:58:51 -0400 Subject: [PATCH 230/428] Trying out test changes --- pennylane_lightning/core/_serialize.py | 31 ++- tests/test_serialize.py | 262 +++++++++++++++++++------ 2 files changed, 222 insertions(+), 71 deletions(-) diff --git a/pennylane_lightning/core/_serialize.py b/pennylane_lightning/core/_serialize.py index 8a81a7bc65..030593ad25 100644 --- a/pennylane_lightning/core/_serialize.py +++ b/pennylane_lightning/core/_serialize.py @@ -35,7 +35,7 @@ from pennylane.math import unwrap from pennylane.operation import Tensor from pennylane.tape import QuantumTape -from pennylane.ops import Prod +from pennylane.ops import Prod, Sum, SProd pauli_name_map = { "I": "Identity", @@ -184,7 +184,7 @@ def _named_obs(self, observable, wires_map: dict = None): def _hermitian_ob(self, observable, wires_map: dict = None): """Serializes a Hermitian observable""" - assert not isinstance(observable, Tensor) + assert not isinstance(observable, (Tensor, Prod)) wires = [wires_map[w] for w in observable.wires] if wires_map else observable.wires.tolist() return self.hermitian_obs(matrix(observable).ravel().astype(self.ctype), wires) @@ -196,8 +196,9 @@ def _tensor_ob(self, observable, wires_map: dict = None): return self.tensor_obs([self._ob(o, wires_map) for o in obs]) def _hamiltonian(self, observable, wires_map: dict = None): - coeffs = np.array(unwrap(observable.coeffs)).astype(self.rtype) - terms = [self._ob(t, wires_map) for t in observable.ops] + coeffs, ops = observable.terms() + coeffs = np.array(unwrap(coeffs)).astype(self.rtype) + terms = [self._ob(t, wires_map) for t in ops] if self.split_obs: return [self.hamiltonian_obs([c], [t]) for (c, t) in zip(coeffs, terms)] @@ -256,23 +257,31 @@ def _pauli_sentence(self, observable, wires_map: dict = None): terms = [self._pauli_word(pw, wires_map) for pw in pwords] coeffs = np.array(coeffs).astype(self.rtype) + # TODO: Add this + # if len(terms) == 1 and coeffs[0] == 1.0: + # return terms[0] + if self.split_obs: return [self.hamiltonian_obs([c], [t]) for (c, t) in zip(coeffs, terms)] return self.hamiltonian_obs(coeffs, terms) - # pylint: disable=protected-access + # pylint: disable=protected-access, too-many-return-statements def _ob(self, observable, wires_map: dict = None): """Serialize a :class:`pennylane.operation.Observable` into an Observable.""" - if isinstance(observable, (PauliX, PauliY, PauliZ, Identity, Hadamard)): - return self._named_obs(observable, wires_map) - if observable._pauli_rep is not None: - return self._pauli_sentence(observable._pauli_rep, wires_map) + if isinstance(observable, (Prod, Sum, SProd)) and observable.pauli_rep is not None: + return self._pauli_sentence(observable.pauli_rep, wires_map) if isinstance(observable, (Tensor, Prod)): return self._tensor_ob(observable, wires_map) if observable.name in ("Hamiltonian", "LinearCombination"): return self._hamiltonian(observable, wires_map) if observable.name == "SparseHamiltonian": return self._sparse_hamiltonian(observable, wires_map) + if isinstance(observable, (PauliX, PauliY, PauliZ, Identity, Hadamard)): + return self._named_obs(observable, wires_map) + if observable.pauli_rep is not None: + return self._pauli_sentence(observable.pauli_rep, wires_map) + # if isinstance(observable, (Prod, Sum)): + # return self._hamiltonian(observable, wires_map) return self._hermitian_ob(observable, wires_map) def serialize_observables(self, tape: QuantumTape, wires_map: dict = None) -> List: @@ -300,7 +309,9 @@ def serialize_observables(self, tape: QuantumTape, wires_map: dict = None) -> Li offset_indices.append(offset_indices[-1] + 1) return serialized_obs, offset_indices - def serialize_ops(self, tape: QuantumTape, wires_map: dict = None) -> Tuple[ + def serialize_ops( + self, tape: QuantumTape, wires_map: dict = None + ) -> Tuple[ List[List[str]], List[np.ndarray], List[List[int]], diff --git a/tests/test_serialize.py b/tests/test_serialize.py index dd5a8386d4..0aa700c66a 100644 --- a/tests/test_serialize.py +++ b/tests/test_serialize.py @@ -24,6 +24,8 @@ global_phase_diagonal, ) +# qml.operation.disable_new_opmath() + if not LightningDevice._CPP_BINARY_AVAILABLE: pytest.skip("No binary module found. Skipping.", allow_module_level=True) @@ -75,16 +77,55 @@ def test_wrong_device_name(): QuantumScriptSerializer("thunder.qubit") +# def get_obs_returns_cases(): +# return [ +# (qml.PauliZ(0), NamedObsC128), +# ( +# qml.PauliZ(0) @ qml.PauliZ(1), +# HamiltonianC128 if qml.operation.active_new_opmath() else TensorProdObsC128, +# ), +# (qml.Hadamard(0), NamedObsC128), +# (qml.Hermitian(np.eye(2), wires=0), HermitianObsC128), +# ( +# qml.PauliZ(0) @ qml.Hadamard(1) @ (0.1 * (qml.PauliZ(2) + qml.PauliX(3))), +# TensorProdObsC128 if qml.operation.active_new_opmath() else HamiltonianC128, +# ), +# ( +# ( +# qml.Hermitian(np.eye(2), wires=0) +# @ qml.Hermitian(np.eye(2), wires=1) +# @ qml.Projector([0], wires=2) +# ), +# TensorProdObsC128, +# ), +# ( +# qml.PauliZ(0) @ qml.Hermitian(np.eye(2), wires=1) @ qml.Projector([0], wires=2), +# TensorProdObsC128, +# ), +# (qml.Projector([0], wires=0), HermitianObsC128), +# (qml.Hamiltonian([1], [qml.PauliZ(0)]), HamiltonianC128), +# (qml.sum(qml.Hadamard(0), qml.PauliX(1)), HermitianObsC128), +# ( +# qml.SparseHamiltonian(qml.Hamiltonian([1], [qml.PauliZ(0)]).sparse_matrix(), wires=[0]), +# SparseHamiltonianC128, +# ), +# ] + + +@pytest.mark.usefixtures("use_legacy_and_new_opmath") @pytest.mark.parametrize( "obs,obs_type", [ (qml.PauliZ(0), NamedObsC128), - (qml.PauliZ(0) @ qml.PauliZ(1), TensorProdObsC128), + ( + qml.PauliZ(0) @ qml.PauliZ(1), + HamiltonianC128 if qml.operation.active_new_opmath() else TensorProdObsC128, + ), (qml.Hadamard(0), NamedObsC128), (qml.Hermitian(np.eye(2), wires=0), HermitianObsC128), ( qml.PauliZ(0) @ qml.Hadamard(1) @ (0.1 * (qml.PauliZ(2) + qml.PauliX(3))), - HamiltonianC128, + TensorProdObsC128 if qml.operation.active_new_opmath() else HamiltonianC128, ), ( ( @@ -112,10 +153,10 @@ def test_obs_returns_expected_type(obs, obs_type): assert isinstance( QuantumScriptSerializer(device_name)._ob(obs, dict(enumerate(obs.wires))), obs_type ) - print(type(QuantumScriptSerializer(device_name)._ob(obs))) assert isinstance(QuantumScriptSerializer(device_name)._ob(obs), obs_type) +@pytest.mark.usefixtures("use_legacy_and_new_opmath") class TestSerializeObs: """Tests for the _observables function""" @@ -130,11 +171,17 @@ def test_tensor_non_tensor_return(self, use_csingle, wires_map): qml.expval(qml.PauliZ(0) @ qml.PauliX(1)) qml.expval(qml.Hadamard(1)) - tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 named_obs = NamedObsC64 if use_csingle else NamedObsC128 + tensor_prod_obs = TensorProdObsC64 if use_csingle else TensorProdObsC128 + first_s = tensor_prod_obs([named_obs("PauliZ", [0]), named_obs("PauliX", [1])]) + + if qml.operation.active_new_opmath(): + ham_obs = HamiltonianC64 if use_csingle else HamiltonianC128 + tensor_obs = tensor_prod_obs([named_obs("PauliX", [1]), named_obs("PauliZ", [0])]) + first_s = ham_obs([1.0], [tensor_obs]) s_expected = [ - tensor_prod_obs([named_obs("PauliZ", [0]), named_obs("PauliX", [1])]), + first_s, named_obs("Hadamard", [1]), ] @@ -188,6 +235,9 @@ def test_hermitian_tensor_return(self, use_csingle, wires_map): assert s[0] == s_expected + # Hamiltonian: { 'coeffs' : [0.3, 0.5, 0.4], 'observables' : [Hermitian @ PauliY[2] @ PauliZ[3], Hamiltonian: { 'coeffs' : [1], 'observables' : [PauliY[2] @ PauliX[0] @ PauliZ[3]]}, Hermitian @ PauliZ[3]]} + # Hamiltonian: { 'coeffs' : [0.3, 0.5, 0.4], 'observables' : [Hermitian @ PauliY[2] @ PauliZ[3], Hamiltonian: { 'coeffs' : [1], 'observables' : [PauliY[2] @ PauliX[0] @ PauliZ[3]]}, Hermitian @ PauliZ[3]]} + @pytest.mark.parametrize("use_csingle", [True, False]) @pytest.mark.parametrize("wires_map", [wires_dict, None]) def test_mixed_tensor_return(self, use_csingle, wires_map): @@ -238,19 +288,37 @@ def test_hamiltonian_return(self, use_csingle, wires_map): tape, wires_map ) - s_expected = hamiltonian_obs( - np.array([0.3, 0.5, 0.4], dtype=r_dtype), - [ - tensor_prod_obs( - [ - hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), - named_obs("PauliY", [2]), - ] - ), - tensor_prod_obs([named_obs("PauliX", [0]), named_obs("PauliY", [2])]), - hermitian_obs(np.ones(64, dtype=c_dtype), [0, 1, 2]), - ], - ) + if qml.operation.active_new_opmath(): + s_expected = hamiltonian_obs( + np.array([0.3, 0.5, 0.4], dtype=r_dtype), + [ + tensor_prod_obs( + [ + hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), + named_obs("PauliY", [2]), + ] + ), + hamiltonian_obs( + np.array([1], dtype=r_dtype), + [tensor_prod_obs([named_obs("PauliY", [2]), named_obs("PauliX", [0])])], + ), + hermitian_obs(np.ones(64, dtype=c_dtype), [0, 1, 2]), + ], + ) + else: + s_expected = hamiltonian_obs( + np.array([0.3, 0.5, 0.4], dtype=r_dtype), + [ + tensor_prod_obs( + [ + hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), + named_obs("PauliY", [2]), + ] + ), + tensor_prod_obs([named_obs("PauliX", [0]), named_obs("PauliY", [2])]), + hermitian_obs(np.ones(64, dtype=c_dtype), [0, 1, 2]), + ], + ) assert s[0] == s_expected @@ -283,24 +351,63 @@ def test_hamiltonian_tensor_return(self, use_csingle, wires_map): # Expression (ham @ obs) is converted internally by Pennylane # where obs is appended to each term of the ham - s_expected = hamiltonian_obs( - np.array([0.3, 0.5, 0.4], dtype=r_dtype), - [ - tensor_prod_obs( - [ - hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), - named_obs("PauliY", [2]), - named_obs("PauliZ", [3]), - ] - ), - tensor_prod_obs( - [named_obs("PauliX", [0]), named_obs("PauliY", [2]), named_obs("PauliZ", [3])] - ), - tensor_prod_obs( - [hermitian_obs(np.ones(64, dtype=c_dtype), [0, 1, 2]), named_obs("PauliZ", [3])] - ), - ], - ) + if qml.operation.active_new_opmath(): + s_expected = hamiltonian_obs( + np.array([0.3, 0.5, 0.4], dtype=r_dtype), + [ + tensor_prod_obs( + [ + hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), + named_obs("PauliY", [2]), + named_obs("PauliZ", [3]), + ] + ), + hamiltonian_obs( + np.array([1], dtype=r_dtype), + [ + tensor_prod_obs( + [ + named_obs("PauliY", [2]), + named_obs("PauliX", [0]), + named_obs("PauliZ", [3]), + ] + ) + ], + ), + tensor_prod_obs( + [ + hermitian_obs(np.ones(64, dtype=c_dtype), [0, 1, 2]), + named_obs("PauliZ", [3]), + ] + ), + ], + ) + else: + s_expected = hamiltonian_obs( + np.array([0.3, 0.5, 0.4], dtype=r_dtype), + [ + tensor_prod_obs( + [ + hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), + named_obs("PauliY", [2]), + named_obs("PauliZ", [3]), + ] + ), + tensor_prod_obs( + [ + named_obs("PauliX", [0]), + named_obs("PauliY", [2]), + named_obs("PauliZ", [3]), + ] + ), + tensor_prod_obs( + [ + hermitian_obs(np.ones(64, dtype=c_dtype), [0, 1, 2]), + named_obs("PauliZ", [3]), + ] + ), + ], + ) assert s[0] == s_expected @@ -337,31 +444,64 @@ def test_hamiltonian_mix_return(self, use_csingle, wires_map): tape, wires_map ) - s_expected1 = hamiltonian_obs( - np.array([0.3, 0.5, 0.4], dtype=r_dtype), - [ - tensor_prod_obs( - [ - hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), - named_obs("PauliY", [2]), - ] - ), - tensor_prod_obs([named_obs("PauliX", [0]), named_obs("PauliY", [2])]), - hermitian_obs(np.ones(64, dtype=c_dtype), [0, 1, 2]), - ], - ) - s_expected2 = hamiltonian_obs( - np.array([0.7, 0.3], dtype=r_dtype), - [ - tensor_prod_obs( - [ - named_obs("PauliX", [0]), - hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [1, 2]), - ] - ), - tensor_prod_obs([named_obs("PauliY", [0]), named_obs("PauliX", [2])]), - ], - ) + if qml.operation.active_new_opmath(): + s_expected1 = hamiltonian_obs( + np.array([0.3, 0.5, 0.4], dtype=r_dtype), + [ + tensor_prod_obs( + [ + hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), + named_obs("PauliY", [2]), + ] + ), + hamiltonian_obs( + np.array([1], dtype=r_dtype), + [tensor_prod_obs([named_obs("PauliY", [2]), named_obs("PauliX", [0])])], + ), + hermitian_obs(np.ones(64, dtype=c_dtype), [0, 1, 2]), + ], + ) + s_expected2 = hamiltonian_obs( + np.array([0.7, 0.3], dtype=r_dtype), + [ + tensor_prod_obs( + [ + named_obs("PauliX", [0]), + hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [1, 2]), + ] + ), + hamiltonian_obs( + np.array([1], dtype=r_dtype), + [tensor_prod_obs([named_obs("PauliX", [2]), named_obs("PauliY", [0])])], + ), + ], + ) + else: + s_expected1 = hamiltonian_obs( + np.array([0.3, 0.5, 0.4], dtype=r_dtype), + [ + tensor_prod_obs( + [ + hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), + named_obs("PauliY", [2]), + ] + ), + tensor_prod_obs([named_obs("PauliX", [0]), named_obs("PauliY", [2])]), + hermitian_obs(np.ones(64, dtype=c_dtype), [0, 1, 2]), + ], + ) + s_expected2 = hamiltonian_obs( + np.array([0.7, 0.3], dtype=r_dtype), + [ + tensor_prod_obs( + [ + named_obs("PauliX", [0]), + hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [1, 2]), + ] + ), + tensor_prod_obs([named_obs("PauliY", [0]), named_obs("PauliX", [2])]), + ], + ) assert s[0] == s_expected1 assert s[1] == s_expected2 From cae72b43fe2d7dc2c30c075aa7fa03ac532bf7e3 Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Wed, 20 Mar 2024 14:06:41 -0400 Subject: [PATCH 231/428] Updated PL dependency --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 801df5fc71..e3954ccf43 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,5 +1,5 @@ pip~=22.0 -git+https://github.com/PennyLaneAI/pennylane.git@getattr-ham-lc-aliasing +git+https://github.com/PennyLaneAI/pennylane.git@ham-tests ninja flaky pybind11 From 1962ea5f2b71c38391481278f15c02fbd1b528fd Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Wed, 20 Mar 2024 14:07:57 -0400 Subject: [PATCH 232/428] Running isort --- pennylane_lightning/core/_serialize.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_serialize.py b/pennylane_lightning/core/_serialize.py index 030593ad25..50501dc8c7 100644 --- a/pennylane_lightning/core/_serialize.py +++ b/pennylane_lightning/core/_serialize.py @@ -34,8 +34,8 @@ ) from pennylane.math import unwrap from pennylane.operation import Tensor +from pennylane.ops import Prod, SProd, Sum from pennylane.tape import QuantumTape -from pennylane.ops import Prod, Sum, SProd pauli_name_map = { "I": "Identity", From 2eeae1a57e9b87cffe2cf6faa5b5248bf3c56555 Mon Sep 17 00:00:00 2001 From: "Lee J. O'Riordan" Date: Wed, 20 Mar 2024 15:11:10 -0400 Subject: [PATCH 233/428] Update CPP layer for observable accessible functions --- pennylane_lightning/core/src/bindings/Bindings.hpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pennylane_lightning/core/src/bindings/Bindings.hpp b/pennylane_lightning/core/src/bindings/Bindings.hpp index 9f06001d54..9ca30968a8 100644 --- a/pennylane_lightning/core/src/bindings/Bindings.hpp +++ b/pennylane_lightning/core/src/bindings/Bindings.hpp @@ -350,6 +350,8 @@ void registerBackendAgnosticObservables(py::module_ &m) { .def("__repr__", &HermitianObs::getObsName) .def("get_wires", &HermitianObs::getWires, "Get wires of observables") + .def("get_matrix", &HermitianObs::getMatrix, + "Get matrix representation of Hermitian operator") .def( "__eq__", [](const HermitianObs &self, @@ -401,6 +403,10 @@ void registerBackendAgnosticObservables(py::module_ &m) { .def("__repr__", &Hamiltonian::getObsName) .def("get_wires", &Hamiltonian::getWires, "Get wires of observables") + .def("get_ops", &Hamiltonian::getObs, + "Get operations contained by Hamiltonian") + .def("get_coeffs", &Hamiltonian::getCoeffs, + "Get Hamiltonian coefficients") .def( "__eq__", [](const Hamiltonian &self, From c6e344d7b829b5d5daed02f98e54f60dfbb9d4b7 Mon Sep 17 00:00:00 2001 From: Dev version update bot Date: Wed, 20 Mar 2024 19:17:54 +0000 Subject: [PATCH 234/428] Auto update version --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index 000566162d..070aab6b47 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.36.0-dev13" +__version__ = "0.36.0-dev14" From a6cf0651771be4b81648bd68da50868a05463177 Mon Sep 17 00:00:00 2001 From: "Lee J. O'Riordan" Date: Wed, 20 Mar 2024 15:27:34 -0400 Subject: [PATCH 235/428] Allow output from TP CPP layer --- pennylane_lightning/core/src/bindings/Bindings.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pennylane_lightning/core/src/bindings/Bindings.hpp b/pennylane_lightning/core/src/bindings/Bindings.hpp index 9ca30968a8..39170e4af2 100644 --- a/pennylane_lightning/core/src/bindings/Bindings.hpp +++ b/pennylane_lightning/core/src/bindings/Bindings.hpp @@ -375,6 +375,8 @@ void registerBackendAgnosticObservables(py::module_ &m) { .def("__repr__", &TensorProdObs::getObsName) .def("get_wires", &TensorProdObs::getWires, "Get wires of observables") + .def("get_ops", &TensorProdObs::getObs, + "Get operations list") .def( "__eq__", [](const TensorProdObs &self, From 0eec0ec219781e873b9a97bfce44d6902326dfc8 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Wed, 20 Mar 2024 15:32:57 -0400 Subject: [PATCH 236/428] initial commit --- .github/workflows/tests_gpu_cuda.yml | 1 - .github/workflows/tests_gpu_kokkos.yml | 4 +- .github/workflows/tests_linux.yml | 5 - .github/workflows/tests_linux_x86_mpi_gpu.yml | 2 +- .github/workflows/tests_windows.yml | 16 ++- CMakeLists.txt | 1 - cmake/process_options.cmake | 16 --- .../tests/Test_MeasurementsBase.cpp | 12 +- .../core/src/observables/Observables.hpp | 12 -- .../observables/tests/Test_Observables.cpp | 24 +--- .../core/src/utils/CMakeLists.txt | 5 +- .../core/src/utils/UtilLinearAlg.hpp | 104 +++++++++++++++--- .../core/src/utils/tests/CMakeLists.txt | 5 +- requirements-dev.txt | 1 + requirements.txt | 3 +- 15 files changed, 115 insertions(+), 96 deletions(-) diff --git a/.github/workflows/tests_gpu_cuda.yml b/.github/workflows/tests_gpu_cuda.yml index 06741a1152..d45c34fe4d 100644 --- a/.github/workflows/tests_gpu_cuda.yml +++ b/.github/workflows/tests_gpu_cuda.yml @@ -128,7 +128,6 @@ jobs: cmake . -BBuild \ -DCUQUANTUM_SDK=$(python -c "import site; print( f'{site.getsitepackages()[0]}/cuquantum')")\ -DBUILD_TESTS=ON \ - -DENABLE_LAPACK=ON \ -DENABLE_PYTHON=OFF \ -DPL_BACKEND=${{ matrix.pl_backend }} \ -DCMAKE_CXX_COMPILER=$(which g++-$GCC_VERSION) \ diff --git a/.github/workflows/tests_gpu_kokkos.yml b/.github/workflows/tests_gpu_kokkos.yml index c31488ad40..89fac0aa43 100644 --- a/.github/workflows/tests_gpu_kokkos.yml +++ b/.github/workflows/tests_gpu_kokkos.yml @@ -147,7 +147,7 @@ jobs: python-version: '3.9' - name: Install dependencies - run: sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION ninja-build gcovr lcov + run: sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION ninja-build gcovr lcov liblapack-dev - name: Build and run unit tests run: | @@ -251,7 +251,7 @@ jobs: python-version: '3.9' - name: Install dependencies - run: sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION + run: sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION liblapack-dev - name: Get required Python packages run: | diff --git a/.github/workflows/tests_linux.yml b/.github/workflows/tests_linux.yml index 4a0c85bd9e..ecf5fdb6be 100644 --- a/.github/workflows/tests_linux.yml +++ b/.github/workflows/tests_linux.yml @@ -54,7 +54,6 @@ jobs: cmake . -BBuild -G Ninja \ -DCMAKE_BUILD_TYPE=Debug \ -DBUILD_TESTS=ON \ - -DENABLE_LAPACK=ON \ -DENABLE_PYTHON=OFF \ -DPL_BACKEND=${{ matrix.pl_backend }} \ -DCMAKE_CXX_COMPILER=$(which g++-$GCC_VERSION) \ @@ -228,7 +227,6 @@ jobs: -DENABLE_BLAS=ON \ -DPL_BACKEND=${{ matrix.pl_backend }} \ -DBUILD_TESTS=ON \ - -DENABLE_LAPACK=ON \ -DCMAKE_CXX_COMPILER=$(which g++-$GCC_VERSION) \ -DENABLE_COVERAGE=ON cmake --build ./Build @@ -283,7 +281,6 @@ jobs: -DENABLE_PYTHON=OFF \ -DPL_BACKEND=${{ matrix.pl_backend }} \ -DBUILD_TESTS=ON \ - -DENABLE_LAPACK=OFF \ -DCMAKE_CXX_COMPILER=$(which g++-$GCC_VERSION) \ -DENABLE_COVERAGE=ON \ -DLQ_ENABLE_KERNEL_OMP=ON @@ -455,7 +452,6 @@ jobs: cmake . -BBuild -G Ninja \ -DCMAKE_BUILD_TYPE=Debug \ -DBUILD_TESTS=ON \ - -DENABLE_LAPACK=ON \ -DENABLE_PYTHON=OFF \ -DCMAKE_PREFIX_PATH=${{ github.workspace }}/Kokkos \ -DPL_BACKEND=${{ matrix.pl_backend }} \ @@ -719,7 +715,6 @@ jobs: cmake . -BBuild -G Ninja \ -DCMAKE_BUILD_TYPE=Debug \ -DBUILD_TESTS=ON \ - -DENABLE_LAPACK=ON \ -DENABLE_PYTHON=OFF \ -DCMAKE_PREFIX_PATH=${{ github.workspace }}/Kokkos \ -DPL_BACKEND="lightning_qubit;lightning_kokkos" \ diff --git a/.github/workflows/tests_linux_x86_mpi_gpu.yml b/.github/workflows/tests_linux_x86_mpi_gpu.yml index 7af97b5046..da4fe1b077 100644 --- a/.github/workflows/tests_linux_x86_mpi_gpu.yml +++ b/.github/workflows/tests_linux_x86_mpi_gpu.yml @@ -118,13 +118,13 @@ jobs: run: | source /etc/profile.d/modules.sh && module use /opt/modules/ && module load ${{ matrix.mpilib }}/cuda-${{ matrix.cuda_version_maj }}.${{ matrix.cuda_version_min }} export CUQUANTUM_SDK=$(python -c "import site; print( f'{site.getsitepackages()[0]}/cuquantum/lib')") + export LD_LIBRARY_PATH=$(python -c "import site; print( f'{site.getsitepackages()[0]}/scipy.libs/')"):$LD_LIBRARY_PATH cmake . -BBuild \ -DPL_BACKEND=lightning_gpu \ -DENABLE_MPI=ON \ -DCMAKE_BUILD_TYPE=Debug \ -DENABLE_COVERAGE=ON \ -DBUILD_TESTS=ON \ - -DENABLE_LAPACK=ON \ -DCMAKE_CXX_COMPILER=mpicxx \ -DCMAKE_CUDA_COMPILER=$(which nvcc) \ -DCMAKE_CUDA_ARCHITECTURES="86" \ diff --git a/.github/workflows/tests_windows.yml b/.github/workflows/tests_windows.yml index 395d4a8bdd..fd236ccca8 100644 --- a/.github/workflows/tests_windows.yml +++ b/.github/workflows/tests_windows.yml @@ -154,6 +154,13 @@ jobs: run: | choco install OpenCppCoverage -y echo "C:\Program Files\OpenCppCoverage" >> $env:GITHUB_PATH + + - name: Get site-packages directory + id: get_site_packages + shell: pwsh + run: | + $pythonSitePackages = python -c "import site; Write-Host site.getsitepackages()[0]" + echo "PYTHON_SITE_PACKAGES=$pythonSitePackages" >> $GITHUB_ENV - name: Build and run unit tests for code coverage run: | @@ -163,7 +170,6 @@ jobs: -DENABLE_PYTHON=OFF ` -DENABLE_GATE_DISPATCHER=OFF ` -DPL_BACKEND=${{ matrix.pl_backend }} ` - -DENABLE_LAPACK=ON ` -DCMAKE_TOOLCHAIN_FILE=D:\a\install_dir\vcpkg\scripts\buildsystems\vcpkg.cmake ` -DENABLE_WARNINGS=OFF cmake --build .\Build --config RelWithDebInfo @@ -241,6 +247,13 @@ jobs: run: | choco install OpenCppCoverage -y echo "C:\Program Files\OpenCppCoverage" >> $env:GITHUB_PATH + + - name: Get site-packages directory + id: get_site_packages + shell: pwsh + run: | + $pythonSitePackages = python -c "import site; Write-Host site.getsitepackages()[0]" + echo "PYTHON_SITE_PACKAGES=$pythonSitePackages" >> $GITHUB_ENV - name: Build and run unit tests for code coverage run: | @@ -254,7 +267,6 @@ jobs: -DCMAKE_TOOLCHAIN_FILE=D:\a\install_dir\vcpkg\scripts\buildsystems\vcpkg.cmake ` -DENABLE_OPENMP=OFF ` -DPL_BACKEND=${{ matrix.pl_backend }} ` - -DENABLE_LAPACK=ON ` -DENABLE_WARNINGS=OFF -T clangcl cmake --build .\Build --config Debug -- /p:UseMultiToolTask=true /p:EnforceProcessCountAcrossBuilds=true /p:MultiProcMaxCount=2 mkdir -p .\Build\tests\results diff --git a/CMakeLists.txt b/CMakeLists.txt index efbe99be3b..523a65da2f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,7 +46,6 @@ option(ENABLE_COVERAGE "Enable code coverage" OFF) option(ENABLE_WARNINGS "Enable warnings" ON) option(ENABLE_NATIVE "Enable native CPU build tuning" OFF) option(ENABLE_PYTHON "Enable compilation of the Python module" ON) -option(ENABLE_LAPACK "Enable compilation of LAPACK" OFF) # OpenMP find_package(OpenMP) diff --git a/cmake/process_options.cmake b/cmake/process_options.cmake index 14809c3597..7feb24b495 100644 --- a/cmake/process_options.cmake +++ b/cmake/process_options.cmake @@ -101,19 +101,3 @@ if (UNIX AND (${CMAKE_SYSTEM_PROCESSOR} MATCHES "(AMD64)|(X64)|(x64)|(x86_64)")) target_compile_options(lightning_compile_options INTERFACE -mavx) endif() - -if(ENABLE_LAPACK) - find_package(LAPACK REQUIRED) - if(LAPACK_FOUND) - message(STATUS "LAPACK found.") - target_link_libraries(lightning_external_libs INTERFACE LAPACK::LAPACK) - target_compile_options(lightning_compile_options INTERFACE "-DPL_USE_LAPACK=1") - else() - if(MSVC) - if(NOT CMAKE_TOOLCHAIN_FILE) - message(FATAL_ERROR "LAPACK is enabled but not found. Please make sure you set CMAKE_TOOLCHAIN_FILE to use the vcpkg toolchain (/scripts/buildsystems/vcpkg.cmake) after vcpkg install LAPACK.\n") - endif() - endif() - message(FATAL_ERROR "LAPACK is enabled but not found.\n") - endif() -endif() diff --git a/pennylane_lightning/core/src/measurements/tests/Test_MeasurementsBase.cpp b/pennylane_lightning/core/src/measurements/tests/Test_MeasurementsBase.cpp index a5f20a3848..a0fb9ed3b2 100644 --- a/pennylane_lightning/core/src/measurements/tests/Test_MeasurementsBase.cpp +++ b/pennylane_lightning/core/src/measurements/tests/Test_MeasurementsBase.cpp @@ -570,7 +570,7 @@ TEST_CASE("Expval Shot- NamedObs", "[MeasurementsBase][Observables]") { } } -#ifdef PL_USE_LAPACK +#ifndef _MSC_VER template void testHermitianObsExpvalShot() { if constexpr (!std::is_same_v) { using StateVectorT = typename TypeList::Type; @@ -839,7 +839,7 @@ template void testTensorProdObsExpvalShot() { expected, static_cast(0.20))); } -#ifdef PL_USE_LAPACK +#ifndef _MSC_VER DYNAMIC_SECTION(" With Identity and shots_range" << StateVectorToName::name) { size_t num_shots = 80000; @@ -1011,7 +1011,7 @@ TEST_CASE("Var - HermitianObs", "[MeasurementsBase][Observables]") { } } -#ifdef PL_USE_LAPACK +#ifndef _MSC_VER template void testHermitianObsShotVar() { if constexpr (!std::is_same_v) { using StateVectorT = typename TypeList::Type; @@ -1148,7 +1148,7 @@ template void testTensorProdObsVarShot() { expected, static_cast(0.20))); } -#ifdef PL_USE_LAPACK +#ifndef _MSC_VER DYNAMIC_SECTION("With Hermitian and NameObs" << StateVectorToName::name) { using MatrixT = std::vector; @@ -1557,7 +1557,7 @@ template void testHamiltonianObsExpvalShot() { expected, static_cast(0.20))); } -#ifdef PL_USE_LAPACK +#ifndef _MSC_VER DYNAMIC_SECTION("YHer" << StateVectorToName::name) { auto Y0 = std::make_shared>( "PauliY", std::vector{0}); @@ -1638,7 +1638,7 @@ template void testHamiltonianObsVarShot() { expected, static_cast(0.20))); } -#ifdef PL_USE_LAPACK +#ifndef _MSC_VER DYNAMIC_SECTION("YHer" << StateVectorToName::name) { using ComplexT = typename StateVectorT::ComplexT; auto Y0 = std::make_shared>( diff --git a/pennylane_lightning/core/src/observables/Observables.hpp b/pennylane_lightning/core/src/observables/Observables.hpp index 884bc2be6f..69b57c187d 100644 --- a/pennylane_lightning/core/src/observables/Observables.hpp +++ b/pennylane_lightning/core/src/observables/Observables.hpp @@ -23,9 +23,7 @@ #include "Error.hpp" #include "Util.hpp" -#ifdef PL_USE_LAPACK #include "UtilLinearAlg.hpp" -#endif namespace Pennylane::Observables { /** @@ -220,12 +218,9 @@ class HermitianObsBase : public Observable { MatrixT matrix_; std::vector wires_; -#ifdef PL_USE_LAPACK - private: std::vector eigenVals_; MatrixT unitary_; -#endif private: [[nodiscard]] auto isEqual(const Observable &other) const @@ -247,7 +242,6 @@ class HermitianObsBase : public Observable { : matrix_{std::move(matrix)}, wires_{std::move(wires)} { PL_ASSERT(matrix_.size() == Util::exp2(2 * wires_.size())); -#ifdef PL_USE_LAPACK std::vector> mat(matrix_.size()); std::transform(matrix_.begin(), matrix_.end(), mat.begin(), @@ -265,7 +259,6 @@ class HermitianObsBase : public Observable { std::transform( unitary.begin(), unitary.end(), unitary_.begin(), [](ComplexT value) { return static_cast(value); }); -#endif } [[nodiscard]] auto getMatrix() const -> const MatrixT & { return matrix_; } @@ -286,7 +279,6 @@ class HermitianObsBase : public Observable { [[maybe_unused]] StateVectorT &sv, [[maybe_unused]] std::vector> &eigenValues, [[maybe_unused]] std::vector &ob_wires) const override { -#ifdef PL_USE_LAPACK std::vector> mat(matrix_.size()); std::transform(matrix_.begin(), matrix_.end(), mat.begin(), @@ -304,10 +296,6 @@ class HermitianObsBase : public Observable { ob_wires = wires_; sv.applyMatrix(unitary_, wires_); eigenValues.push_back(eigenVals_); -#else - PL_ABORT("Hermitian observables do not support shot measurement. " - "Please link against Lapack."); -#endif } }; diff --git a/pennylane_lightning/core/src/observables/tests/Test_Observables.cpp b/pennylane_lightning/core/src/observables/tests/Test_Observables.cpp index 694d0a6b22..30a5dd4edd 100644 --- a/pennylane_lightning/core/src/observables/tests/Test_Observables.cpp +++ b/pennylane_lightning/core/src/observables/tests/Test_Observables.cpp @@ -207,7 +207,7 @@ template void testHermitianObsBase() { REQUIRE(ob2 != ob3); } -#ifdef PL_USE_LAPACK +#ifndef _MSC_VER DYNAMIC_SECTION("Failed to create a HermitianObs- " << StateVectorToName::name) { std::mt19937_64 re{1337}; @@ -229,28 +229,6 @@ template void testHermitianObsBase() { } #endif -#ifndef PL_USE_LAPACK - DYNAMIC_SECTION("Failed for HermitianObs for applyInPlaceShots - " - << StateVectorToName::name) { - std::mt19937_64 re{1337}; - constexpr size_t num_qubits = 3; - auto init_state = - createRandomStateVectorData(re, num_qubits); - - StateVectorT state_vector(init_state.data(), init_state.size()); - auto obs = - HermitianObsT{std::vector{1.0, 0.0, -1.0, 0.0}, {0}}; - - std::vector> eigenValues; - std::vector ob_wires; - - REQUIRE_THROWS_WITH( - obs.applyInPlaceShots(state_vector, eigenValues, ob_wires), - Catch::Matchers::Contains( - "Hermitian observables do not support shot measurement.")); - } -#endif - testHermitianObsBase(); } } diff --git a/pennylane_lightning/core/src/utils/CMakeLists.txt b/pennylane_lightning/core/src/utils/CMakeLists.txt index ac86df6f5c..d17412e80d 100644 --- a/pennylane_lightning/core/src/utils/CMakeLists.txt +++ b/pennylane_lightning/core/src/utils/CMakeLists.txt @@ -2,11 +2,8 @@ cmake_minimum_required(VERSION 3.20) project(lightning_utils LANGUAGES CXX) -set(LQUBIT_UTILS_FILES RuntimeInfo.cpp CACHE INTERNAL "" FORCE) +set(LQUBIT_UTILS_FILES RuntimeInfo.cpp UtilLinearAlg.cpp CACHE INTERNAL "" FORCE) -if(ENABLE_LAPACK) - list(APPEND LQUBIT_UTILS_FILES UtilLinearAlg.cpp) -endif() add_library(lightning_utils STATIC ${LQUBIT_UTILS_FILES}) diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index 55fcddb187..981891bdb8 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -23,19 +23,24 @@ #include #include +#if defined(__APPLE__) || defined(__linux__) +#include +#elif defined(_MSC_VER) +#include +#include +#include +#endif + /// @cond DEV namespace { -extern "C" { // LAPACK routine for complex Hermitian eigensystems -extern void cheev_(const char *jobz, const char *uplo, const int *n, - std::complex *a, const int *lda, float *w, - std::complex *work, const int *lwork, float *rwork, - int *info); -extern void zheev_(const char *jobz, const char *uplo, const int *n, - std::complex *a, const int *lda, double *w, - std::complex *work, const int *lwork, double *rwork, - int *info); -} +typedef void (*zheevPtr)(const char *, const char *, const int *, + std::complex *, const int *, double *, + std::complex *, const int *, double *, int *); +typedef void (*cheevPtr)(const char *, const char *, const int *, + std::complex *, const int *, float *, + std::complex *, const int *, float *, int *); + } // namespace /// @endcond @@ -52,6 +57,7 @@ namespace Pennylane::Util { * @param eigenVals eigenvalue results. * @param unitaries unitary result. */ + template void compute_diagonalizing_gates(int n, int lda, const std::vector> &Ah, @@ -69,6 +75,50 @@ void compute_diagonalizing_gates(int n, int lda, ah[j * n + i] = Ah[i * lda + j]; } } +#ifdef __APPLE__ + void *handle = + dlopen("/System/Library/Frameworks/Accelerate.framework/Versions/" + "Current/Frameworks/vecLib.framework/libLAPACK.dylib", + RTLD_LAZY | RTLD_GLOBAL); + if (!handle) { + handle = dlopen("/usr/local/opt/lapack/lib/liblapack.dylib", + RTLD_LAZY | RTLD_GLOBAL); + } + if (!handle) { + fprintf(stderr, "%s\n", dlerror()); + } +#elif defined(__linux__) + void *handle = dlopen("liblapack.so", RTLD_LAZY | RTLD_GLOBAL); +#elif defined(_MSC_VER) + const char *PythonSitePackagePath = std::getenv("PYTHON_SITE_PACKAGES"); + std::string openblasLib; + if (PythonSitePackagePath != nullptr) { + std::filesystem::path scipyLibsPath(PythonSitePackagePath); + scipyLibsPath = scipyLibsPath / "scipy.libs"; + for (const auto &lib : + std::filesystem::directory_iterator(scipyLibsPath)) { + if (lib.is_regular_file()) { + if (lib.path().filename().find("openblas") != + std::string::npos) { + openblasLib = lib.path().filename().c_str(); + } + } + } + } else { + auto currentPath = std::filesystem::currentPath(); + auto scipyLibsPath = currentPath.parent_path() / "scipy.libs"; + for (const auto &lib : + std::filesystem::directory_iterator(scipyLibsPath)) { + if (lib.is_regular_file()) { + if (lib.path().filename().find("openblas") != + std::string::npos) { + openblasLib = lib.path().filename().c_str(); + } + } + } + } + HMODULE handle = LoadLibrary(openblasLib.c_str()); +#endif char jobz = 'V'; // Enable both eigenvalues and eigenvectors computation char uplo = 'L'; // Upper triangle of matrix is stored @@ -78,27 +128,45 @@ void compute_diagonalizing_gates(int n, int lda, int info; if constexpr (std::is_same::value) { +#if defined(__APPLE__) || defined(__linux__) + cheevPtr cheev = reinterpret_cast(dlsym(handle, "cheev_")); +#elif defined(_MSC_VER) + cheevPtr cheev = + reinterpret_cast(GetProcAddress(handle, "cheev_")); +#endif // Query optimal workspace size - cheev_(&jobz, &uplo, &n, ah.data(), &lda, eigenVals.data(), - work_query.data(), &lwork, rwork.data(), &info); + cheev(&jobz, &uplo, &n, ah.data(), &lda, eigenVals.data(), + work_query.data(), &lwork, rwork.data(), &info); // Allocate workspace lwork = static_cast(work_query[0].real()); std::vector> work_optimal(lwork, {0, 0}); // Perform eigenvalue and eigenvector computation - cheev_(&jobz, &uplo, &n, ah.data(), &lda, eigenVals.data(), - work_optimal.data(), &lwork, rwork.data(), &info); + cheev(&jobz, &uplo, &n, ah.data(), &lda, eigenVals.data(), + work_optimal.data(), &lwork, rwork.data(), &info); } else { +#if defined(__APPLE__) || defined(__linux__) + zheevPtr zheev = reinterpret_cast(dlsym(handle, "zheev_")); +#elif defined(_MSC_VER) + zheevPtr zheev = + reinterpret_cast(GetProcAddress(handle, "zheev_")); +#endif // Query optimal workspace size - zheev_(&jobz, &uplo, &n, ah.data(), &lda, eigenVals.data(), - work_query.data(), &lwork, rwork.data(), &info); + zheev(&jobz, &uplo, &n, ah.data(), &lda, eigenVals.data(), + work_query.data(), &lwork, rwork.data(), &info); // Allocate workspace lwork = static_cast(work_query[0].real()); std::vector> work_optimal(lwork, {0, 0}); // Perform eigenvalue and eigenvector computation - zheev_(&jobz, &uplo, &n, ah.data(), &lda, eigenVals.data(), - work_optimal.data(), &lwork, rwork.data(), &info); + zheev(&jobz, &uplo, &n, ah.data(), &lda, eigenVals.data(), + work_optimal.data(), &lwork, rwork.data(), &info); } +#if defined(__APPLE__) || defined(__linux__) + dlclose(handle); +#elif defined(_MSC_VER) + FreeLibrary(handle); +#endif + std::transform(ah.begin(), ah.end(), unitary.begin(), [](std::complex value) { return std::complex{value.real(), -value.imag()}; diff --git a/pennylane_lightning/core/src/utils/tests/CMakeLists.txt b/pennylane_lightning/core/src/utils/tests/CMakeLists.txt index ccb0c8d062..4b9d5e01b9 100644 --- a/pennylane_lightning/core/src/utils/tests/CMakeLists.txt +++ b/pennylane_lightning/core/src/utils/tests/CMakeLists.txt @@ -32,12 +32,9 @@ set(TEST_SOURCES Test_BitUtil.cpp Test_RuntimeInfo.cpp Test_TypeTraits.cpp Test_Util.cpp + Test_UtilLinearAlg.cpp ) -if(ENABLE_LAPACK) - list(APPEND TEST_SOURCES Test_UtilLinearAlg.cpp) -endif() - add_executable(utils_test_runner ${TEST_SOURCES}) target_link_libraries(utils_test_runner PRIVATE utils_tests) diff --git a/requirements-dev.txt b/requirements-dev.txt index e9467a9e54..7c56461162 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -17,3 +17,4 @@ click==8.0.4 cmake custatevec-cu12 pylint +scipy diff --git a/requirements.txt b/requirements.txt index c888135fa8..ee821aafd0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,4 +4,5 @@ pennylane>=0.34 pybind11 pytest~=8.0.0 pytest-cov -pytest-mock \ No newline at end of file +pytest-mock +scipy \ No newline at end of file From c08dd474e712a005bce74d45a8a29218ccfcf529 Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Wed, 20 Mar 2024 15:33:05 -0400 Subject: [PATCH 237/428] Fixed serialize tests --- tests/test_adjoint_jacobian.py | 2 +- tests/test_serialize.py | 8 ++++++-- tests/test_vjp.py | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/test_adjoint_jacobian.py b/tests/test_adjoint_jacobian.py index 463f648cd8..7354a50b12 100644 --- a/tests/test_adjoint_jacobian.py +++ b/tests/test_adjoint_jacobian.py @@ -215,7 +215,7 @@ def test_proj_unsupported(self, dev): with qml.tape.QuantumTape() as tape: qml.CRX(0.1, wires=[0, 1]) - qml.expval(qml.Projector([0], wires=[0]) @ qml.PauliZ(0)) + qml.expval(qml.Projector([0], wires=[0]) @ qml.PauliZ(1)) with pytest.raises( qml.QuantumFunctionError, match="differentiation method does not support the Projector" diff --git a/tests/test_serialize.py b/tests/test_serialize.py index 0aa700c66a..5be7cb6549 100644 --- a/tests/test_serialize.py +++ b/tests/test_serialize.py @@ -357,8 +357,12 @@ def test_hamiltonian_tensor_return(self, use_csingle, wires_map): [ tensor_prod_obs( [ - hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), - named_obs("PauliY", [2]), + tensor_prod_obs( + [ + hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), + named_obs("PauliY", [2]), + ] + ), named_obs("PauliZ", [3]), ] ), diff --git a/tests/test_vjp.py b/tests/test_vjp.py index 70bd091bf9..2344e91547 100644 --- a/tests/test_vjp.py +++ b/tests/test_vjp.py @@ -203,7 +203,7 @@ def test_proj_unsupported(self, dev): with qml.tape.QuantumTape() as tape: qml.CRX(0.1, wires=[0, 1]) - qml.expval(qml.Projector([0], wires=[0]) @ qml.PauliZ(0)) + qml.expval(qml.Projector([0], wires=[0]) @ qml.PauliZ(1)) with pytest.raises( qml.QuantumFunctionError, From 383f402209ab5df777b59386f796fefb121eabb7 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Wed, 20 Mar 2024 15:51:49 -0400 Subject: [PATCH 238/428] fix windows ci --- .github/workflows/tests_windows.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests_windows.yml b/.github/workflows/tests_windows.yml index fd236ccca8..d5e962500a 100644 --- a/.github/workflows/tests_windows.yml +++ b/.github/workflows/tests_windows.yml @@ -248,7 +248,7 @@ jobs: choco install OpenCppCoverage -y echo "C:\Program Files\OpenCppCoverage" >> $env:GITHUB_PATH - - name: Get site-packages directory + - name: Get site-packages directory id: get_site_packages shell: pwsh run: | From b3021a950cb8e48eeafc3640fac45517d2e288f2 Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Wed, 20 Mar 2024 16:35:40 -0400 Subject: [PATCH 239/428] Added legacy fixture to failing tests --- tests/test_adjoint_jacobian.py | 1 + tests/test_vjp.py | 1 + 2 files changed, 2 insertions(+) diff --git a/tests/test_adjoint_jacobian.py b/tests/test_adjoint_jacobian.py index 7354a50b12..8ebef21411 100644 --- a/tests/test_adjoint_jacobian.py +++ b/tests/test_adjoint_jacobian.py @@ -198,6 +198,7 @@ def test_unsupported_op(self, dev): ): dev.adjoint_jacobian(tape) + @pytest.mark.usefixtures("use_legacy_opmath") @pytest.mark.skipif(ld._new_API, reason="Old API required") @pytest.mark.skipif(not ld._CPP_BINARY_AVAILABLE, reason="Lightning binary required") def test_proj_unsupported(self, dev): diff --git a/tests/test_vjp.py b/tests/test_vjp.py index 2344e91547..d90ccb4fac 100644 --- a/tests/test_vjp.py +++ b/tests/test_vjp.py @@ -186,6 +186,7 @@ def test_unsupported_op(self, dev): ): dev.vjp(tape.measurements, dy)(tape) + @pytest.mark.usefixtures("use_legacy_opmath") def test_proj_unsupported(self, dev): """Test if a QuantumFunctionError is raised for a Projector observable""" From 208255a63ad97ab50332959a5a5e269f53900f00 Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Thu, 21 Mar 2024 09:57:29 -0400 Subject: [PATCH 240/428] Updated requirements with debug branch --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index e3954ccf43..9a6e0fb57e 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,5 +1,5 @@ pip~=22.0 -git+https://github.com/PennyLaneAI/pennylane.git@ham-tests +git+https://github.com/PennyLaneAI/pennylane.git@lightning-op-math ninja flaky pybind11 From 692b57adfe2d1a23b76aa8e1063536c581567003 Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Thu, 21 Mar 2024 10:16:02 -0400 Subject: [PATCH 241/428] Trigger CI From 40bc003673acc3fd3ad0431fd95d6a344a06fe4b Mon Sep 17 00:00:00 2001 From: Dev version update bot Date: Thu, 21 Mar 2024 14:18:46 +0000 Subject: [PATCH 242/428] Auto update version --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index 070aab6b47..134b71b540 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.36.0-dev14" +__version__ = "0.36.0-dev15" From 685a75203ca18d5753114384eda52c748d3b4df9 Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Thu, 21 Mar 2024 10:27:36 -0400 Subject: [PATCH 243/428] Trigger CI From fef7060f48987b9eac2672b0ca3223100fb2016a Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Thu, 21 Mar 2024 11:09:58 -0400 Subject: [PATCH 244/428] Trigger CI From d36b9a6f4866b3b460082d5da28eb3360e4f6cde Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Thu, 21 Mar 2024 12:13:15 -0400 Subject: [PATCH 245/428] add priority table for scipy.libs loading --- .github/workflows/tests_windows.yml | 4 +-- .../core/src/utils/UtilLinearAlg.hpp | 33 +++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests_windows.yml b/.github/workflows/tests_windows.yml index d5e962500a..777cd3cd0c 100644 --- a/.github/workflows/tests_windows.yml +++ b/.github/workflows/tests_windows.yml @@ -159,7 +159,7 @@ jobs: id: get_site_packages shell: pwsh run: | - $pythonSitePackages = python -c "import site; Write-Host site.getsitepackages()[0]" + $pythonSitePackages = python -c "import site; print(site.getsitepackages()[0])" echo "PYTHON_SITE_PACKAGES=$pythonSitePackages" >> $GITHUB_ENV - name: Build and run unit tests for code coverage @@ -252,7 +252,7 @@ jobs: id: get_site_packages shell: pwsh run: | - $pythonSitePackages = python -c "import site; Write-Host site.getsitepackages()[0]" + $pythonSitePackages = python -c "import site; print(site.getsitepackages()[0])" echo "PYTHON_SITE_PACKAGES=$pythonSitePackages" >> $GITHUB_ENV - name: Build and run unit tests for code coverage diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index 981891bdb8..0b2cc6b686 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -89,6 +89,39 @@ void compute_diagonalizing_gates(int n, int lda, } #elif defined(__linux__) void *handle = dlopen("liblapack.so", RTLD_LAZY | RTLD_GLOBAL); + if (!handle) { + std::unordered_map priority_lib = { + {"stdc", 0}, + {"gcc", 1}, + {"quadmath", 2}, + {"gfortran", 3}, + {"openblas", 4}}; + auto currentPath = std::filesystem::currentPath(); + auto scipyLibsPath = currentPath.parent_path() / "scipy.libs"; + std::vector> availableLibs; + for (const auto &lib : + std::filesystem::directory_iterator(scipyLibsPath)) { + if (lib.is_regular_file()) { + for (auto &iter : priority_lib) { + if (lib.path().filename().find(iter->first) != std::string + : npos) { + availableLibs.emplace_back( + {lib.path().filename().c_str(), iter->second}); + } + } + } + } + + std::sort(availableLibs.begin(), availableLibs.end(), + [](const auto &lhs, const auto &rhs) { + return lhs.second < rhs.second; + }); + + for (auto &lib : availableLibs) { + handle = dlopen(lib->first.c_str(), RTLD_LAZY | RTLD_GLOBAL); + } + } + #elif defined(_MSC_VER) const char *PythonSitePackagePath = std::getenv("PYTHON_SITE_PACKAGES"); std::string openblasLib; From b72737aba6f056ded4bae1bcc2eb5b4e3b3e4d9d Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Thu, 21 Mar 2024 13:23:29 -0400 Subject: [PATCH 246/428] add filesytem header --- pennylane_lightning/core/src/utils/UtilLinearAlg.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index 0b2cc6b686..890ff813ec 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -21,6 +21,9 @@ #include #include +#include +#include +#include #include #if defined(__APPLE__) || defined(__linux__) From bbee87b97a9488f8e894ee7e788544a7c42bc116 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Thu, 21 Mar 2024 15:16:31 -0400 Subject: [PATCH 247/428] fix libLoad for linux --- .../core/src/utils/UtilLinearAlg.hpp | 39 ++++++++++++------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index 890ff813ec..cbeea5175f 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -44,6 +44,8 @@ typedef void (*cheevPtr)(const char *, const char *, const int *, std::complex *, const int *, float *, std::complex *, const int *, float *, int *); +std::unordered_map priority_lib = { + {"stdc", 0}, {"gcc", 1}, {"quadmath", 2}, {"gfortran", 3}, {"openblas", 4}}; } // namespace /// @endcond @@ -79,6 +81,7 @@ void compute_diagonalizing_gates(int n, int lda, } } #ifdef __APPLE__ + std::vector handles; void *handle = dlopen("/System/Library/Frameworks/Accelerate.framework/Versions/" "Current/Frameworks/vecLib.framework/libLAPACK.dylib", @@ -90,26 +93,25 @@ void compute_diagonalizing_gates(int n, int lda, if (!handle) { fprintf(stderr, "%s\n", dlerror()); } + handles.push_back(handle); #elif defined(__linux__) - void *handle = dlopen("liblapack.so", RTLD_LAZY | RTLD_GLOBAL); + std::vector handles; + void *handle; + + handle = dlopen("liblapack.so", RTLD_LAZY | RTLD_GLOBAL); + if (!handle) { - std::unordered_map priority_lib = { - {"stdc", 0}, - {"gcc", 1}, - {"quadmath", 2}, - {"gfortran", 3}, - {"openblas", 4}}; - auto currentPath = std::filesystem::currentPath(); + auto currentPath = std::filesystem::current_path(); auto scipyLibsPath = currentPath.parent_path() / "scipy.libs"; std::vector> availableLibs; for (const auto &lib : std::filesystem::directory_iterator(scipyLibsPath)) { if (lib.is_regular_file()) { - for (auto &iter : priority_lib) { - if (lib.path().filename().find(iter->first) != std::string - : npos) { + for (const auto &iter : priority_lib) { + std::string libname_str = lib.path().filename(); + if (libname_str.find(iter.first) != std::string ::npos) { availableLibs.emplace_back( - {lib.path().filename().c_str(), iter->second}); + std::make_pair(libname_str, iter.second)); } } } @@ -120,11 +122,15 @@ void compute_diagonalizing_gates(int n, int lda, return lhs.second < rhs.second; }); - for (auto &lib : availableLibs) { - handle = dlopen(lib->first.c_str(), RTLD_LAZY | RTLD_GLOBAL); + for (const auto &lib : availableLibs) { + auto libPath = scipyLibsPath / lib.first.c_str(); + handle = dlopen(libPath.c_str(), RTLD_LAZY | RTLD_GLOBAL); + if (!handle) { + fprintf(stderr, "%s\n", dlerror()); + } + handles.push_back(handle); } } - #elif defined(_MSC_VER) const char *PythonSitePackagePath = std::getenv("PYTHON_SITE_PACKAGES"); std::string openblasLib; @@ -199,6 +205,9 @@ void compute_diagonalizing_gates(int n, int lda, #if defined(__APPLE__) || defined(__linux__) dlclose(handle); + for (auto handle : handles) { + dlclose(handle); + } #elif defined(_MSC_VER) FreeLibrary(handle); #endif From e6809c8b471de87991d9fb971fe9e599e9cb970a Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Thu, 21 Mar 2024 15:20:32 -0400 Subject: [PATCH 248/428] Added recursive logic to find projectors --- pennylane_lightning/core/lightning_base.py | 60 +++++++++++++++++-- .../lightning_gpu/lightning_gpu.py | 41 +------------ .../lightning_kokkos/lightning_kokkos.py | 45 +------------- .../lightning_qubit/lightning_qubit.py | 22 +++++++ tests/new_api/test_device.py | 22 +++++++ tests/test_adjoint_jacobian.py | 2 +- tests/test_vjp.py | 2 +- 7 files changed, 104 insertions(+), 90 deletions(-) diff --git a/pennylane_lightning/core/lightning_base.py b/pennylane_lightning/core/lightning_base.py index 650fb0ed84..6104091a5e 100644 --- a/pennylane_lightning/core/lightning_base.py +++ b/pennylane_lightning/core/lightning_base.py @@ -23,8 +23,9 @@ import pennylane as qml from pennylane import BasisState, QubitDevice, StatePrep from pennylane.devices import DefaultQubitLegacy -from pennylane.measurements import MeasurementProcess -from pennylane.operation import Operation +from pennylane.measurements import MeasurementProcess, State, Expectation +from pennylane.operation import Operation, Tensor +from pennylane.ops import Projector, Sum, Prod, SProd from pennylane.wires import Wires from ._serialize import QuantumScriptSerializer @@ -306,18 +307,69 @@ def _process_jacobian_tape( "obs_idx_offsets": obs_idx_offsets, } + @staticmethod + def _assert_adjdiff_no_projectors(observable): + """Helper function to validate that an observable is not or does not contain + Projectors + + Args: + observable (.Operator): Observable to check + + Raises: + ~pennylane.QuantumFunctionError: if a ``Projector`` is found. + """ + if isinstance(observable, Tensor): + if any(isinstance(o, Projector) for o in observable.non_identity_obs): + raise qml.QuantumFunctionError( + "Adjoint differentiation method does not support the Projector observable" + ) + + elif isinstance(observable, Projector): + raise qml.QuantumFunctionError( + "Adjoint differentiation method does not support the Projector observable" + ) + + elif isinstance(observable, SProd): + LightningBase._assert_adjdiff_no_projectors(observable.base) + + elif isinstance(observable, (Sum, Prod)): + for obs in observable: + LightningBase._assert_adjdiff_no_projectors(obs) + # pylint: disable=unnecessary-pass @staticmethod def _check_adjdiff_supported_measurements(measurements: List[MeasurementProcess]): - """Check whether given list of measurement is supported by adjoint_differentiation. + """Check whether given list of measurements is supported by adjoint_differentiation. Args: measurements (List[MeasurementProcess]): a list of measurement processes to check. Returns: Expectation or State: a common return type of measurements. + + Raises: + ~pennylane.QuantumFunctionError: if a measurement is unsupported with adjoint + differentiation. """ - pass + if not measurements: + return None + + if len(measurements) == 1 and measurements[0].return_type is State: + # return State + raise qml.QuantumFunctionError( + "Adjoint differentiation does not support State measurements." + ) + + # The return_type of measurement processes must be expectation + if any(m.return_type is not Expectation for m in measurements): + raise qml.QuantumFunctionError( + "Adjoint differentiation method does not support expectation return type " + "mixed with other return types" + ) + + for measurement in measurements: + LightningBase._assert_adjdiff_no_projectors(measurement.obs) + return Expectation @staticmethod def _adjoint_jacobian_processing(jac): diff --git a/pennylane_lightning/lightning_gpu/lightning_gpu.py b/pennylane_lightning/lightning_gpu/lightning_gpu.py index a23bd085f4..b877d9503f 100644 --- a/pennylane_lightning/lightning_gpu/lightning_gpu.py +++ b/pennylane_lightning/lightning_gpu/lightning_gpu.py @@ -80,14 +80,12 @@ from pennylane import ( BasisState, DeviceError, - Projector, QuantumFunctionError, Rot, StatePrep, math, ) - from pennylane.measurements import Expectation, MeasurementProcess, State - from pennylane.operation import Tensor + from pennylane.measurements import Expectation, State from pennylane.ops.op_math import Adjoint from pennylane.wires import Wires @@ -587,43 +585,6 @@ def apply(self, operations, rotations=None, **kwargs): self.apply_lightning(operations) - @staticmethod - def _check_adjdiff_supported_measurements(measurements: List[MeasurementProcess]): - """Check whether given list of measurement is supported by adjoint_diff. - Args: - measurements (List[MeasurementProcess]): a list of measurement processes to check. - Returns: - Expectation or State: a common return type of measurements. - """ - if not measurements: - return None - - if len(measurements) == 1 and measurements[0].return_type is State: - # return State - raise QuantumFunctionError( - "Adjoint differentiation does not support State measurements." - ) - - # The return_type of measurement processes must be expectation - if any(m.return_type is not Expectation for m in measurements): - raise QuantumFunctionError( - "Adjoint differentiation method does not support expectation return type " - "mixed with other return types" - ) - - for measurement in measurements: - if isinstance(measurement.obs, Tensor): - if any(isinstance(o, Projector) for o in measurement.obs.non_identity_obs): - raise QuantumFunctionError( - "Adjoint differentiation method does not support the " - "Projector observable" - ) - elif isinstance(measurement.obs, Projector): - raise QuantumFunctionError( - "Adjoint differentiation method does not support the Projector observable" - ) - return Expectation - @staticmethod def _check_adjdiff_supported_operations(operations): """Check Lightning adjoint differentiation method support for a tape. diff --git a/pennylane_lightning/lightning_kokkos/lightning_kokkos.py b/pennylane_lightning/lightning_kokkos/lightning_kokkos.py index a61566a4c0..7e51710972 100644 --- a/pennylane_lightning/lightning_kokkos/lightning_kokkos.py +++ b/pennylane_lightning/lightning_kokkos/lightning_kokkos.py @@ -53,14 +53,12 @@ from pennylane import ( BasisState, DeviceError, - Projector, QuantumFunctionError, Rot, StatePrep, math, ) - from pennylane.measurements import Expectation, MeasurementProcess, State - from pennylane.operation import Tensor + from pennylane.measurements import Expectation, State from pennylane.ops.op_math import Adjoint from pennylane.wires import Wires @@ -628,47 +626,6 @@ def sample(self, observable, shot_range=None, bin_size=None, counts=False): self.apply([qml.adjoint(g, lazy=False) for g in reversed(diagonalizing_gates)]) return results - @staticmethod - def _check_adjdiff_supported_measurements( - measurements: List[MeasurementProcess], - ): - """Check whether given list of measurement is supported by adjoint_differentiation. - - Args: - measurements (List[MeasurementProcess]): a list of measurement processes to check. - - Returns: - Expectation or State: a common return type of measurements. - """ - if not measurements: - return None - - if len(measurements) == 1 and measurements[0].return_type is State: - # return State - raise QuantumFunctionError( - "Adjoint differentiation does not support State measurements." - ) - - # Now the return_type of measurement processes must be expectation - if any(m.return_type is not Expectation for m in measurements): - raise QuantumFunctionError( - "Adjoint differentiation method does not support expectation return type " - "mixed with other return types" - ) - - for measurement in measurements: - if isinstance(measurement.obs, Tensor): - if any(isinstance(o, Projector) for o in measurement.obs.non_identity_obs): - raise QuantumFunctionError( - "Adjoint differentiation method does not support the " - "Projector observable" - ) - elif isinstance(measurement.obs, Projector): - raise QuantumFunctionError( - "Adjoint differentiation method does not support the Projector observable" - ) - return Expectation - @staticmethod def _check_adjdiff_supported_operations(operations): """Check Lightning adjoint differentiation method support for a tape. diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit.py b/pennylane_lightning/lightning_qubit/lightning_qubit.py index 48ba784a2a..d5ccf52332 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit.py @@ -31,6 +31,8 @@ validate_measurements, validate_observables, ) +from pennylane.operation import Tensor +from pennylane.ops import Sum, Prod, SProd from pennylane.tape import QuantumScript, QuantumTape from pennylane.transforms.core import TransformProgram from pennylane.typing import Result, ResultBatch @@ -236,6 +238,26 @@ def accepted_observables(obs: qml.operation.Operator) -> bool: return obs.name in _observables +def adjoint_observables(obs: qml.operation.Operator) -> bool: + """A function that determines whether or not an observable is supported by ``lightning.qubit`` + when using the adjoint differentiation method.""" + if isinstance(obs, qml.Projector): + return False + + if isinstance(obs, Tensor): + if any(isinstance(o, qml.Projector) for o in obs.non_identity_obs): + return False + return True + + if isinstance(obs, SProd): + return adjoint_observables(obs.base) + + if isinstance(obs, (Sum, Prod)): + return all(adjoint_observables(o) for o in obs) + + return obs.name in _observables + + def adjoint_measurements(mp: qml.measurements.MeasurementProcess) -> bool: """Specifies whether or not an observable is compatible with adjoint differentiation on DefaultQubit.""" return isinstance(mp, qml.measurements.ExpectationMP) diff --git a/tests/new_api/test_device.py b/tests/new_api/test_device.py index 6407ed60b5..af3aee1bbd 100644 --- a/tests/new_api/test_device.py +++ b/tests/new_api/test_device.py @@ -28,6 +28,7 @@ _add_adjoint_transforms, _supports_adjoint, accepted_observables, + adjoint_observables, adjoint_measurements, decompose, no_sampling, @@ -76,6 +77,27 @@ def test_accepted_observables(self): assert accepted_observables(valid_obs) is True assert accepted_observables(invalid_obs) is False + @pytest.mark.parametrize( + "obs, expected", + [ + (qml.operation.Tensor(qml.Projector([0], 0), qml.PauliZ(1)), False), + (qml.prod(qml.Projector([0], 0), qml.PauliZ(1)), False), + (qml.s_prod(1.5, qml.Projector([0], 0)), False), + (qml.sum(qml.Projector([0], 0), qml.Hadamard(1)), False), + (qml.sum(qml.prod(qml.Projector([0], 0), qml.Y(1)), qml.PauliX(1)), False), + (qml.operation.Tensor(qml.Y(0), qml.Z(1)), True), + (qml.prod(qml.Y(0), qml.PauliZ(1)), True), + (qml.s_prod(1.5, qml.Y(1)), True), + (qml.sum(qml.Y(1), qml.Hadamard(1)), True), + (qml.X(0), True), + (qml.Hermitian(np.eye(4), [0, 1]), True), + ], + ) + def test_adjoint_observables(self, obs, expected): + """Test that adjoint_observables returns the expected boolean result for + a given observable""" + assert adjoint_observables(obs) == expected + def test_add_adjoint_transforms(self): """Test that the correct transforms are added to the program by _add_adjoint_transforms""" expected_program = qml.transforms.core.TransformProgram() diff --git a/tests/test_adjoint_jacobian.py b/tests/test_adjoint_jacobian.py index 8ebef21411..18a0149d13 100644 --- a/tests/test_adjoint_jacobian.py +++ b/tests/test_adjoint_jacobian.py @@ -198,7 +198,7 @@ def test_unsupported_op(self, dev): ): dev.adjoint_jacobian(tape) - @pytest.mark.usefixtures("use_legacy_opmath") + @pytest.mark.usefixtures("use_legacy_and_new_opmath") @pytest.mark.skipif(ld._new_API, reason="Old API required") @pytest.mark.skipif(not ld._CPP_BINARY_AVAILABLE, reason="Lightning binary required") def test_proj_unsupported(self, dev): diff --git a/tests/test_vjp.py b/tests/test_vjp.py index d90ccb4fac..8d2b3765cb 100644 --- a/tests/test_vjp.py +++ b/tests/test_vjp.py @@ -186,7 +186,7 @@ def test_unsupported_op(self, dev): ): dev.vjp(tape.measurements, dy)(tape) - @pytest.mark.usefixtures("use_legacy_opmath") + @pytest.mark.usefixtures("use_legacy_and_new_opmath") def test_proj_unsupported(self, dev): """Test if a QuantumFunctionError is raised for a Projector observable""" From 7fcb1191e8333230ec4ecf69018b3178f19b9cc4 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Thu, 21 Mar 2024 15:34:24 -0400 Subject: [PATCH 249/428] format --- pennylane_lightning/core/lightning_base.py | 4 ++-- .../lightning_kokkos/lightning_kokkos.py | 14 +++++++------- .../lightning_qubit/lightning_qubit.py | 2 +- tests/new_api/test_device.py | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/pennylane_lightning/core/lightning_base.py b/pennylane_lightning/core/lightning_base.py index 6104091a5e..9d28b87e6e 100644 --- a/pennylane_lightning/core/lightning_base.py +++ b/pennylane_lightning/core/lightning_base.py @@ -23,9 +23,9 @@ import pennylane as qml from pennylane import BasisState, QubitDevice, StatePrep from pennylane.devices import DefaultQubitLegacy -from pennylane.measurements import MeasurementProcess, State, Expectation +from pennylane.measurements import Expectation, MeasurementProcess, State from pennylane.operation import Operation, Tensor -from pennylane.ops import Projector, Sum, Prod, SProd +from pennylane.ops import Prod, Projector, SProd, Sum from pennylane.wires import Wires from ._serialize import QuantumScriptSerializer diff --git a/pennylane_lightning/lightning_kokkos/lightning_kokkos.py b/pennylane_lightning/lightning_kokkos/lightning_kokkos.py index 7e51710972..f2aab30011 100644 --- a/pennylane_lightning/lightning_kokkos/lightning_kokkos.py +++ b/pennylane_lightning/lightning_kokkos/lightning_kokkos.py @@ -62,6 +62,13 @@ from pennylane.ops.op_math import Adjoint from pennylane.wires import Wires + # pylint: disable=import-error, no-name-in-module, ungrouped-imports + from pennylane_lightning.core._serialize import ( + QuantumScriptSerializer, + global_phase_diagonal, + ) + from pennylane_lightning.core._version import __version__ + # pylint: disable=import-error, no-name-in-module, ungrouped-imports from pennylane_lightning.lightning_kokkos_ops.algorithms import ( AdjointJacobianC64, @@ -70,13 +77,6 @@ create_ops_listC128, ) - # pylint: disable=import-error, no-name-in-module, ungrouped-imports - from pennylane_lightning.core._serialize import ( - QuantumScriptSerializer, - global_phase_diagonal, - ) - from pennylane_lightning.core._version import __version__ - def _kokkos_dtype(dtype): if dtype not in [np.complex128, np.complex64]: # pragma: no cover raise ValueError(f"Data type is not supported for state-vector computation: {dtype}") diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit.py b/pennylane_lightning/lightning_qubit/lightning_qubit.py index d5ccf52332..f7e25f4c4e 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit.py @@ -32,7 +32,7 @@ validate_observables, ) from pennylane.operation import Tensor -from pennylane.ops import Sum, Prod, SProd +from pennylane.ops import Prod, SProd, Sum from pennylane.tape import QuantumScript, QuantumTape from pennylane.transforms.core import TransformProgram from pennylane.typing import Result, ResultBatch diff --git a/tests/new_api/test_device.py b/tests/new_api/test_device.py index af3aee1bbd..fd63125d7c 100644 --- a/tests/new_api/test_device.py +++ b/tests/new_api/test_device.py @@ -28,8 +28,8 @@ _add_adjoint_transforms, _supports_adjoint, accepted_observables, - adjoint_observables, adjoint_measurements, + adjoint_observables, decompose, no_sampling, stopping_condition, From 1c0b841249fa0e64fef2da6d659618b4b0fd7395 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Thu, 21 Mar 2024 15:35:18 -0400 Subject: [PATCH 250/428] mark tests to be dual tested and some small fixes --- .../test_adjoint_jacobian_class.py | 2 + tests/test_adjoint_jacobian.py | 19 ++++----- tests/test_execute.py | 1 + tests/test_serialize.py | 42 +------------------ tests/test_vjp.py | 5 ++- 5 files changed, 15 insertions(+), 54 deletions(-) diff --git a/tests/lightning_qubit/test_adjoint_jacobian_class.py b/tests/lightning_qubit/test_adjoint_jacobian_class.py index 9b1dd435a8..bc25d1e1a6 100644 --- a/tests/lightning_qubit/test_adjoint_jacobian_class.py +++ b/tests/lightning_qubit/test_adjoint_jacobian_class.py @@ -252,6 +252,7 @@ def test_multiple_rx_gradient_expval_hermitian(self, tol, lightning_sv): assert np.allclose(expected, result, atol=tol, rtol=0) + @pytest.mark.usefixtures("use_legacy_and_new_opmath") def test_multiple_rx_gradient_expval_hamiltonian(self, tol, lightning_sv): """Tests that the gradient of multiple RX gates in a circuit yields the correct result with Hermitian observable @@ -362,6 +363,7 @@ def calculate_vjp(statevector, tape, vector): return LightningAdjointJacobian(statevector).calculate_vjp(tape, vector) + @pytest.mark.usefixtures("use_legacy_and_new_opmath") def test_multiple_measurements(self, tol, lightning_sv): """Tests provides correct answer when provided multiple measurements.""" x, y, z = [0.5, 0.3, -0.7] diff --git a/tests/test_adjoint_jacobian.py b/tests/test_adjoint_jacobian.py index 18a0149d13..94226696ee 100644 --- a/tests/test_adjoint_jacobian.py +++ b/tests/test_adjoint_jacobian.py @@ -402,6 +402,7 @@ def test_multiple_rx_gradient_expval_hermitian(self, tol, dev): qubit_ops = [getattr(qml, name) for name in qml.ops._qubit__ops__] ops = {qml.RX, qml.RY, qml.RZ, qml.PhaseShift, qml.CRX, qml.CRY, qml.CRZ, qml.Rot} + @pytest.mark.usefixtures("use_legacy_and_new_opmath") @pytest.mark.skipif(not ld._CPP_BINARY_AVAILABLE, reason="Lightning binary required") def test_multiple_rx_gradient_expval_hamiltonian(self, tol, dev): """Tests that the gradient of multiple RX gates in a circuit yields the correct result @@ -580,6 +581,7 @@ def test_gradient_gate_with_multiple_parameters_hermitian(self, dev): # the different methods agree assert np.allclose(grad_D, grad_F, atol=tol, rtol=0) + @pytest.mark.usefixtures("use_legacy_and_new_opmath") @pytest.mark.skipif(not ld._CPP_BINARY_AVAILABLE, reason="Lightning binary required") def test_gradient_gate_with_multiple_parameters_hamiltonian(self, dev): """Tests that gates with multiple free parameters yield correct gradients.""" @@ -1131,10 +1133,8 @@ def circuit_ansatz(params, wires): qml.RX(params[29], wires=wires[1]) -@pytest.mark.skipif( - device_name != "lightning.gpu" or not ld._CPP_BINARY_AVAILABLE, - reason="Lightning binary required", -) +@pytest.mark.usefixtures("use_legacy_and_new_opmath") +@pytest.mark.skipif(not ld._CPP_BINARY_AVAILABLE, reason="Lightning binary required") def test_tape_qchem(tol): """Tests the circuit Ansatz with a QChem Hamiltonian produces correct results""" @@ -1157,6 +1157,7 @@ def circuit(params): assert np.allclose(qml.grad(circuit_ld)(params), qml.grad(circuit_dq)(params), tol) +@pytest.mark.usefixtures("use_legacy_and_new_opmath") @pytest.mark.skipif(not ld._CPP_BINARY_AVAILABLE, reason="Lightning binary required") def test_tape_qchem_sparse(tol): """Tests the circuit Ansatz with a QChem Hamiltonian produces correct results""" @@ -1502,14 +1503,8 @@ def create_xyz_file(tmp_path_factory): yield file -@pytest.mark.skipif( - not ld._CPP_BINARY_AVAILABLE, - reason="Tests only for lightning.gpu", -) -@pytest.mark.parametrize( - "batches", - [False, True, 1, 2, 3, 4], -) +@pytest.mark.skipif(not ld._CPP_BINARY_AVAILABLE, reason="Lightning binary required") +@pytest.mark.parametrize("batches", [False, True, 1, 2, 3, 4]) def test_integration_H2_Hamiltonian(create_xyz_file, batches): _ = pytest.importorskip("openfermionpyscf") n_electrons = 2 diff --git a/tests/test_execute.py b/tests/test_execute.py index 631ecc5b65..82f3c52a78 100644 --- a/tests/test_execute.py +++ b/tests/test_execute.py @@ -25,6 +25,7 @@ pytest.skip("No binary module found. Skipping.", allow_module_level=True) +@pytest.mark.usefixtures("use_legacy_and_new_opmath") @pytest.mark.parametrize("diff_method", ("param_shift", "finite_diff")) class TestQChem: """Test tapes returning the expectation values of a Hamiltonian, with a qchem workflow.""" diff --git a/tests/test_serialize.py b/tests/test_serialize.py index 5be7cb6549..7d85b9314f 100644 --- a/tests/test_serialize.py +++ b/tests/test_serialize.py @@ -24,8 +24,6 @@ global_phase_diagonal, ) -# qml.operation.disable_new_opmath() - if not LightningDevice._CPP_BINARY_AVAILABLE: pytest.skip("No binary module found. Skipping.", allow_module_level=True) @@ -77,41 +75,6 @@ def test_wrong_device_name(): QuantumScriptSerializer("thunder.qubit") -# def get_obs_returns_cases(): -# return [ -# (qml.PauliZ(0), NamedObsC128), -# ( -# qml.PauliZ(0) @ qml.PauliZ(1), -# HamiltonianC128 if qml.operation.active_new_opmath() else TensorProdObsC128, -# ), -# (qml.Hadamard(0), NamedObsC128), -# (qml.Hermitian(np.eye(2), wires=0), HermitianObsC128), -# ( -# qml.PauliZ(0) @ qml.Hadamard(1) @ (0.1 * (qml.PauliZ(2) + qml.PauliX(3))), -# TensorProdObsC128 if qml.operation.active_new_opmath() else HamiltonianC128, -# ), -# ( -# ( -# qml.Hermitian(np.eye(2), wires=0) -# @ qml.Hermitian(np.eye(2), wires=1) -# @ qml.Projector([0], wires=2) -# ), -# TensorProdObsC128, -# ), -# ( -# qml.PauliZ(0) @ qml.Hermitian(np.eye(2), wires=1) @ qml.Projector([0], wires=2), -# TensorProdObsC128, -# ), -# (qml.Projector([0], wires=0), HermitianObsC128), -# (qml.Hamiltonian([1], [qml.PauliZ(0)]), HamiltonianC128), -# (qml.sum(qml.Hadamard(0), qml.PauliX(1)), HermitianObsC128), -# ( -# qml.SparseHamiltonian(qml.Hamiltonian([1], [qml.PauliZ(0)]).sparse_matrix(), wires=[0]), -# SparseHamiltonianC128, -# ), -# ] - - @pytest.mark.usefixtures("use_legacy_and_new_opmath") @pytest.mark.parametrize( "obs,obs_type", @@ -235,9 +198,6 @@ def test_hermitian_tensor_return(self, use_csingle, wires_map): assert s[0] == s_expected - # Hamiltonian: { 'coeffs' : [0.3, 0.5, 0.4], 'observables' : [Hermitian @ PauliY[2] @ PauliZ[3], Hamiltonian: { 'coeffs' : [1], 'observables' : [PauliY[2] @ PauliX[0] @ PauliZ[3]]}, Hermitian @ PauliZ[3]]} - # Hamiltonian: { 'coeffs' : [0.3, 0.5, 0.4], 'observables' : [Hermitian @ PauliY[2] @ PauliZ[3], Hamiltonian: { 'coeffs' : [1], 'observables' : [PauliY[2] @ PauliX[0] @ PauliZ[3]]}, Hermitian @ PauliZ[3]]} - @pytest.mark.parametrize("use_csingle", [True, False]) @pytest.mark.parametrize("wires_map", [wires_dict, None]) def test_mixed_tensor_return(self, use_csingle, wires_map): @@ -325,7 +285,7 @@ def test_hamiltonian_return(self, use_csingle, wires_map): @pytest.mark.parametrize("use_csingle", [True, False]) @pytest.mark.parametrize("wires_map", [wires_dict, None]) def test_hamiltonian_tensor_return(self, use_csingle, wires_map): - """Test expected serialization for a Hamiltonian return""" + """Test expected serialization for a tensor Hamiltonian return""" with qml.tape.QuantumTape() as tape: ham = qml.Hamiltonian( diff --git a/tests/test_vjp.py b/tests/test_vjp.py index 8d2b3765cb..56af2adef9 100644 --- a/tests/test_vjp.py +++ b/tests/test_vjp.py @@ -98,8 +98,11 @@ def test_multiple_measurements(self, tol, dev): dy = np.array([1.0, 2.0, 3.0]) tape1.trainable_params = {1, 2, 3} + ham = qml.Hamiltonian(dy, [qml.PauliX(0), qml.PauliY(1), qml.PauliY(1)]) + print(ham) + with qml.tape.QuantumTape() as tape2: - ham = qml.Hamiltonian(dy, [qml.PauliX(0), qml.PauliY(1), qml.PauliY(1)]) + ham qml.RX(0.4, wires=[0]) qml.Rot(x, y, z, wires=[0]) qml.RY(-0.2, wires=[0]) From ace21bd4390936b810d2a40916580f892a6f0db2 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Thu, 21 Mar 2024 15:35:34 -0400 Subject: [PATCH 251/428] example of test failing --- tests/test_measurements.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_measurements.py b/tests/test_measurements.py index a1bfa173c4..1b564791c9 100644 --- a/tests/test_measurements.py +++ b/tests/test_measurements.py @@ -298,6 +298,7 @@ def circuit(): assert np.allclose(circuit(), cases[1], atol=tol, rtol=0) + @pytest.mark.usefixtures("use_legacy_and_new_opmath") @pytest.mark.parametrize( "obs, coeffs, res", [ From 451adf8fa727fccecf51d8da9972b390c36fdc66 Mon Sep 17 00:00:00 2001 From: AmintorDusko Date: Thu, 21 Mar 2024 15:50:25 -0400 Subject: [PATCH 252/428] remove print and move ham to tape scope --- tests/test_vjp.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/test_vjp.py b/tests/test_vjp.py index 56af2adef9..8d2b3765cb 100644 --- a/tests/test_vjp.py +++ b/tests/test_vjp.py @@ -98,11 +98,8 @@ def test_multiple_measurements(self, tol, dev): dy = np.array([1.0, 2.0, 3.0]) tape1.trainable_params = {1, 2, 3} - ham = qml.Hamiltonian(dy, [qml.PauliX(0), qml.PauliY(1), qml.PauliY(1)]) - print(ham) - with qml.tape.QuantumTape() as tape2: - ham + ham = qml.Hamiltonian(dy, [qml.PauliX(0), qml.PauliY(1), qml.PauliY(1)]) qml.RX(0.4, wires=[0]) qml.Rot(x, y, z, wires=[0]) qml.RY(-0.2, wires=[0]) From 5d4bd26f062f1f3f085fc790758d41e181fa6e27 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Thu, 21 Mar 2024 15:56:47 -0400 Subject: [PATCH 253/428] fix currentPath -> current_path --- pennylane_lightning/core/src/utils/UtilLinearAlg.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index cbeea5175f..4644d5af84 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -147,7 +147,7 @@ void compute_diagonalizing_gates(int n, int lda, } } } else { - auto currentPath = std::filesystem::currentPath(); + auto currentPath = std::filesystem::current_path(); auto scipyLibsPath = currentPath.parent_path() / "scipy.libs"; for (const auto &lib : std::filesystem::directory_iterator(scipyLibsPath)) { From e332555c175e65eb04c1e3a943cceed91750d1f5 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Thu, 21 Mar 2024 16:02:59 -0400 Subject: [PATCH 254/428] fix for windows scipy libs iteration --- pennylane_lightning/core/src/utils/UtilLinearAlg.hpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index 4644d5af84..14c74d4f19 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -26,6 +26,8 @@ #include #include +#include + #if defined(__APPLE__) || defined(__linux__) #include #elif defined(_MSC_VER) @@ -137,10 +139,12 @@ void compute_diagonalizing_gates(int n, int lda, if (PythonSitePackagePath != nullptr) { std::filesystem::path scipyLibsPath(PythonSitePackagePath); scipyLibsPath = scipyLibsPath / "scipy.libs"; + std::cout << scipyLibsPath << std::endl; for (const auto &lib : std::filesystem::directory_iterator(scipyLibsPath)) { if (lib.is_regular_file()) { - if (lib.path().filename().find("openblas") != + std::string libname_str = lib.path().filename(); + if (libname_str.find("openblas") != std::string::npos) { openblasLib = lib.path().filename().c_str(); } @@ -149,10 +153,12 @@ void compute_diagonalizing_gates(int n, int lda, } else { auto currentPath = std::filesystem::current_path(); auto scipyLibsPath = currentPath.parent_path() / "scipy.libs"; + std::cout << scipyLibsPath << std::endl; for (const auto &lib : std::filesystem::directory_iterator(scipyLibsPath)) { if (lib.is_regular_file()) { - if (lib.path().filename().find("openblas") != + std::string libname_str = lib.path().filename(); + if (libname_str.find("openblas") != std::string::npos) { openblasLib = lib.path().filename().c_str(); } From f49711dc5721d3ce57a6e8a880ea611e07b38bea Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Thu, 21 Mar 2024 16:08:53 -0400 Subject: [PATCH 255/428] windows fix --- pennylane_lightning/core/src/utils/UtilLinearAlg.hpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index 14c74d4f19..e37173378b 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -146,7 +146,7 @@ void compute_diagonalizing_gates(int n, int lda, std::string libname_str = lib.path().filename(); if (libname_str.find("openblas") != std::string::npos) { - openblasLib = lib.path().filename().c_str(); + openblasLib = libname_str; } } } @@ -160,12 +160,13 @@ void compute_diagonalizing_gates(int n, int lda, std::string libname_str = lib.path().filename(); if (libname_str.find("openblas") != std::string::npos) { - openblasLib = lib.path().filename().c_str(); + openblasLib = libname_str; } } } } - HMODULE handle = LoadLibrary(openblasLib.c_str()); + auto libPath = scipyLibsPath / openblasLib.c_str(); + HMODULE handle = LoadLibrary(libPath.c_str()); #endif char jobz = 'V'; // Enable both eigenvalues and eigenvectors computation From bed31fd6a4063c97a4d7bca7da4bfe9e34b4fe10 Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Thu, 21 Mar 2024 16:25:31 -0400 Subject: [PATCH 256/428] Fixed expval test; isort --- .../lightning_kokkos/lightning_kokkos.py | 14 +++++++------- tests/test_measurements.py | 5 +++++ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/pennylane_lightning/lightning_kokkos/lightning_kokkos.py b/pennylane_lightning/lightning_kokkos/lightning_kokkos.py index f2aab30011..7e51710972 100644 --- a/pennylane_lightning/lightning_kokkos/lightning_kokkos.py +++ b/pennylane_lightning/lightning_kokkos/lightning_kokkos.py @@ -62,13 +62,6 @@ from pennylane.ops.op_math import Adjoint from pennylane.wires import Wires - # pylint: disable=import-error, no-name-in-module, ungrouped-imports - from pennylane_lightning.core._serialize import ( - QuantumScriptSerializer, - global_phase_diagonal, - ) - from pennylane_lightning.core._version import __version__ - # pylint: disable=import-error, no-name-in-module, ungrouped-imports from pennylane_lightning.lightning_kokkos_ops.algorithms import ( AdjointJacobianC64, @@ -77,6 +70,13 @@ create_ops_listC128, ) + # pylint: disable=import-error, no-name-in-module, ungrouped-imports + from pennylane_lightning.core._serialize import ( + QuantumScriptSerializer, + global_phase_diagonal, + ) + from pennylane_lightning.core._version import __version__ + def _kokkos_dtype(dtype): if dtype not in [np.complex128, np.complex64]: # pragma: no cover raise ValueError(f"Data type is not supported for state-vector computation: {dtype}") diff --git a/tests/test_measurements.py b/tests/test_measurements.py index 1b564791c9..181271f97d 100644 --- a/tests/test_measurements.py +++ b/tests/test_measurements.py @@ -324,6 +324,11 @@ def circuit(): ) def test_expval_hamiltonian(self, obs, coeffs, res, tol, dev): """Test expval with Hamiltonian""" + if not qml.operation.active_new_opmath(): + obs = [ + qml.operation.convert_to_legacy_H(o).ops[0] if isinstance(o, qml.ops.Prod) else o + for o in obs + ] ham = qml.Hamiltonian(coeffs, obs) @qml.qnode(dev) From df44376d0608417e815312ad2cbbd358fd0795b6 Mon Sep 17 00:00:00 2001 From: Dev version update bot Date: Thu, 21 Mar 2024 20:25:58 +0000 Subject: [PATCH 257/428] Auto update version --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index 134b71b540..778353eb5b 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.36.0-dev15" +__version__ = "0.36.0-dev16" From de85f995bd97f38f0ec72540ffd6141d82029719 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Thu, 21 Mar 2024 16:27:10 -0400 Subject: [PATCH 258/428] fix windows --- pennylane_lightning/core/src/utils/UtilLinearAlg.hpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index e37173378b..ed7bd8b9bf 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -136,8 +136,10 @@ void compute_diagonalizing_gates(int n, int lda, #elif defined(_MSC_VER) const char *PythonSitePackagePath = std::getenv("PYTHON_SITE_PACKAGES"); std::string openblasLib; + std::filesystem::path scipyLibsPath; if (PythonSitePackagePath != nullptr) { - std::filesystem::path scipyLibsPath(PythonSitePackagePath); + std::filesystem::path tmpPath(PythonSitePackagePath); + scipyLibsPath=tmpPath; scipyLibsPath = scipyLibsPath / "scipy.libs"; std::cout << scipyLibsPath << std::endl; for (const auto &lib : @@ -152,7 +154,7 @@ void compute_diagonalizing_gates(int n, int lda, } } else { auto currentPath = std::filesystem::current_path(); - auto scipyLibsPath = currentPath.parent_path() / "scipy.libs"; + scipyLibsPath = currentPath.parent_path() / "scipy.libs"; std::cout << scipyLibsPath << std::endl; for (const auto &lib : std::filesystem::directory_iterator(scipyLibsPath)) { From 1ee6a95246ba000f7a641d7863fc1c3a5d8a05b9 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Thu, 21 Mar 2024 16:27:53 -0400 Subject: [PATCH 259/428] quick fix --- pennylane_lightning/core/src/utils/UtilLinearAlg.hpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index ed7bd8b9bf..edc0881cdf 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -139,15 +139,14 @@ void compute_diagonalizing_gates(int n, int lda, std::filesystem::path scipyLibsPath; if (PythonSitePackagePath != nullptr) { std::filesystem::path tmpPath(PythonSitePackagePath); - scipyLibsPath=tmpPath; + scipyLibsPath = tmpPath; scipyLibsPath = scipyLibsPath / "scipy.libs"; std::cout << scipyLibsPath << std::endl; for (const auto &lib : std::filesystem::directory_iterator(scipyLibsPath)) { if (lib.is_regular_file()) { std::string libname_str = lib.path().filename(); - if (libname_str.find("openblas") != - std::string::npos) { + if (libname_str.find("openblas") != std::string::npos) { openblasLib = libname_str; } } @@ -160,8 +159,7 @@ void compute_diagonalizing_gates(int n, int lda, std::filesystem::directory_iterator(scipyLibsPath)) { if (lib.is_regular_file()) { std::string libname_str = lib.path().filename(); - if (libname_str.find("openblas") != - std::string::npos) { + if (libname_str.find("openblas") != std::string::npos) { openblasLib = libname_str; } } From 83643a59bdf48da1f4a0c35fc9e9421e7e5839c0 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Thu, 21 Mar 2024 16:37:14 -0400 Subject: [PATCH 260/428] std::filesystem::path::value_type* LPCSTR convert --- pennylane_lightning/core/src/utils/UtilLinearAlg.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index edc0881cdf..e930383fd4 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -166,7 +166,7 @@ void compute_diagonalizing_gates(int n, int lda, } } auto libPath = scipyLibsPath / openblasLib.c_str(); - HMODULE handle = LoadLibrary(libPath.c_str()); + HMODULE handle = LoadLibrary(libPath.string().c_str()); #endif char jobz = 'V'; // Enable both eigenvalues and eigenvectors computation From a54aa96d43d6be9e6aebca09a86f719aefd3e5a6 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Thu, 21 Mar 2024 16:59:20 -0400 Subject: [PATCH 261/428] windows type convert --- pennylane_lightning/core/src/utils/UtilLinearAlg.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index e930383fd4..b538d15c51 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -145,7 +145,7 @@ void compute_diagonalizing_gates(int n, int lda, for (const auto &lib : std::filesystem::directory_iterator(scipyLibsPath)) { if (lib.is_regular_file()) { - std::string libname_str = lib.path().filename(); + std::string libname_str = lib.path().filename().string(); if (libname_str.find("openblas") != std::string::npos) { openblasLib = libname_str; } @@ -158,7 +158,7 @@ void compute_diagonalizing_gates(int n, int lda, for (const auto &lib : std::filesystem::directory_iterator(scipyLibsPath)) { if (lib.is_regular_file()) { - std::string libname_str = lib.path().filename(); + std::string libname_str = lib.path().filename().string(); if (libname_str.find("openblas") != std::string::npos) { openblasLib = libname_str; } From 44f85e729d9c48c7cafad45ca95590ea324dbff5 Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Thu, 21 Mar 2024 16:57:45 -0400 Subject: [PATCH 262/428] isort --- pennylane_lightning/lightning_gpu/lightning_gpu.py | 9 +-------- pennylane_lightning/lightning_kokkos/lightning_kokkos.py | 9 +-------- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/pennylane_lightning/lightning_gpu/lightning_gpu.py b/pennylane_lightning/lightning_gpu/lightning_gpu.py index cf1f64eb2d..c1a0fd605e 100644 --- a/pennylane_lightning/lightning_gpu/lightning_gpu.py +++ b/pennylane_lightning/lightning_gpu/lightning_gpu.py @@ -77,14 +77,7 @@ from typing import List, Union import pennylane as qml - from pennylane import ( - BasisState, - DeviceError, - QuantumFunctionError, - Rot, - StatePrep, - math, - ) + from pennylane import BasisState, DeviceError, QuantumFunctionError, Rot, StatePrep, math from pennylane.measurements import Expectation, State from pennylane.ops.op_math import Adjoint from pennylane.wires import Wires diff --git a/pennylane_lightning/lightning_kokkos/lightning_kokkos.py b/pennylane_lightning/lightning_kokkos/lightning_kokkos.py index 488cb47944..f330ff69fa 100644 --- a/pennylane_lightning/lightning_kokkos/lightning_kokkos.py +++ b/pennylane_lightning/lightning_kokkos/lightning_kokkos.py @@ -50,14 +50,7 @@ from typing import List import pennylane as qml - from pennylane import ( - BasisState, - DeviceError, - QuantumFunctionError, - Rot, - StatePrep, - math, - ) + from pennylane import BasisState, DeviceError, QuantumFunctionError, Rot, StatePrep, math from pennylane.measurements import Expectation, State from pennylane.ops.op_math import Adjoint from pennylane.wires import Wires From 802842a8b39f978b271b3c559a7bf401710b5d4b Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Thu, 21 Mar 2024 17:46:37 -0400 Subject: [PATCH 263/428] Updated device tests; changelog entry --- .github/CHANGELOG.md | 3 +++ tests/new_api/test_device.py | 33 +++++++++++++++++++++++++-------- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index 7fd030a55d..1912f2165e 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -21,6 +21,9 @@ [(#607)](https://github.com/PennyLaneAI/pennylane-lightning/pull/607) [(#628)](https://github.com/PennyLaneAI/pennylane-lightning/pull/628) +* Add support for using new operator arithmetic as the default. + [(#649)](https://github.com/PennyLaneAI/pennylane-lightning/pull/649) + ### Breaking changes * Migrate `lightning.qubit` to the new device API. diff --git a/tests/new_api/test_device.py b/tests/new_api/test_device.py index fd63125d7c..722e21a29e 100644 --- a/tests/new_api/test_device.py +++ b/tests/new_api/test_device.py @@ -283,6 +283,7 @@ def test_preprocess(self, adjoint): actual_program, _ = device.preprocess(config) assert actual_program == expected_program + @pytest.mark.usefixtures("use_legacy_and_new_opmath") @pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI))) @pytest.mark.parametrize( "mp", @@ -291,11 +292,11 @@ def test_preprocess(self, adjoint): qml.probs(op=qml.Z(2)), qml.expval(qml.Z(2)), qml.var(qml.X(2)), - qml.expval(qml.sum(qml.X(0), qml.Z(0))), + qml.expval(qml.X(0) + qml.Z(0)), qml.expval(qml.Hamiltonian([-0.5, 1.5], [qml.Y(1), qml.X(1)])), - qml.expval(qml.s_prod(2.5, qml.Z(0))), - qml.expval(qml.prod(qml.Z(0), qml.X(1))), - qml.expval(qml.sum(qml.Z(1), qml.X(1))), + qml.expval(2.5 * qml.Z(0)), + qml.expval(qml.Z(0) @ qml.X(1)), + qml.expval(qml.operation.Tensor(qml.Z(0), qml.X(1))), qml.expval( qml.SparseHamiltonian( qml.Hamiltonian([-1.0, 1.5], [qml.Z(1), qml.X(1)]).sparse_matrix( @@ -309,6 +310,9 @@ def test_preprocess(self, adjoint): ) def test_execute_single_measurement(self, theta, phi, mp, dev): """Test that execute returns the correct results with a single measurement.""" + if isinstance(mp.obs, qml.ops.LinearCombination) and not qml.operation.active_new_opmath(): + mp.obs = qml.operation.convert_to_legacy_H(mp.obs) + qs = QuantumScript( [ qml.RX(phi, 0), @@ -322,6 +326,7 @@ def test_execute_single_measurement(self, theta, phi, mp, dev): expected = self.calculate_reference(qs)[0] assert np.allclose(res, expected) + @pytest.mark.usefixtures("use_legacy_and_new_opmath") @pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI))) @pytest.mark.parametrize( "mp1", @@ -343,6 +348,9 @@ def test_execute_single_measurement(self, theta, phi, mp, dev): ) def test_execute_multi_measurement(self, theta, phi, dev, mp1, mp2): """Test that execute returns the correct results with multiple measurements.""" + if isinstance(mp2.obs, qml.ops.LinearCombination) and not qml.operation.active_new_opmath(): + mp2.obs = qml.operation.convert_to_legacy_H(mp2.obs) + qs = QuantumScript( [ qml.RX(phi, 0), @@ -454,18 +462,19 @@ def test_supports_derivatives(self, dev, config, tape, expected, batch_obs): """Test that supports_derivative returns the correct boolean value.""" assert dev.supports_derivatives(config, tape) == expected + @pytest.mark.usefixtures("use_legacy_and_new_opmath") @pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI))) @pytest.mark.parametrize( "obs", [ qml.Z(1), - qml.s_prod(2.5, qml.Z(0)), - qml.prod(qml.Z(0), qml.X(1)), - qml.sum(qml.Z(1), qml.X(1)), + 2.5 * qml.Z(0), + qml.Z(0) @ qml.X(1), + qml.operation.Tensor(qml.Z(0), qml.X(1)), + qml.Z(1) + qml.X(1), qml.Hamiltonian([-1.0, 1.5], [qml.Z(1), qml.X(1)]), qml.Hermitian(qml.Hadamard.compute_matrix(), 0), qml.Projector([1], 1), - qml.operation.Tensor(qml.Z(0), qml.X(1)), ], ) @pytest.mark.parametrize("execute_and_derivatives", [True, False]) @@ -473,6 +482,9 @@ def test_derivatives_single_expval( self, theta, phi, dev, obs, execute_and_derivatives, batch_obs ): """Test that the jacobian is correct when a tape has a single expectation value""" + if isinstance(obs, qml.ops.LinearCombination) and not qml.operation.active_new_opmath(): + obs = qml.operation.convert_to_legacy_H(obs) + qs = QuantumScript( [qml.RX(theta, 0), qml.CNOT([0, 1]), qml.RY(phi, 1)], [qml.expval(obs)], @@ -526,6 +538,11 @@ def test_derivatives_multi_expval( self, theta, phi, omega, dev, obs1, obs2, execute_and_derivatives, batch_obs ): """Test that the jacobian is correct when a tape has multiple expectation values""" + if isinstance(obs1, qml.ops.LinearCombination) and not qml.operation.active_new_opmath(): + obs1 = qml.operation.convert_to_legacy_H(obs1) + if isinstance(obs2, qml.ops.LinearCombination) and not qml.operation.active_new_opmath(): + obs2 = qml.operation.convert_to_legacy_H(obs2) + qs = QuantumScript( [ qml.RX(theta, 0), From 996a26f4351915dd77e3eda7b7aed19822ed0207 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Thu, 21 Mar 2024 18:48:44 -0400 Subject: [PATCH 264/428] get current path of dso with dlfcn apis --- .../core/src/utils/UtilLinearAlg.hpp | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index b538d15c51..faef41499f 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -48,6 +48,24 @@ typedef void (*cheevPtr)(const char *, const char *, const int *, std::unordered_map priority_lib = { {"stdc", 0}, {"gcc", 1}, {"quadmath", 2}, {"gfortran", 3}, {"openblas", 4}}; +#ifdef __linux__ +const char *getPath() { + Dl_info dl_info; + if (dladdr((const void *)getPath, &dl_info) != 0) { + return dl_info.dli_fname; + } else { + return nullptr; + } +} +#elif defined(_MSC_VER) +std::string getPath() { + char buffer[MAX_PATH]; + GetModuleFileName(nullptr, buffer, MAX_PATH); + std::string fullPath(buffer); + std::size_t pos = fullPath.find_last_of("\\/"); + return fullPath.substr(0, pos); +} +#endif } // namespace /// @endcond @@ -103,8 +121,9 @@ void compute_diagonalizing_gates(int n, int lda, handle = dlopen("liblapack.so", RTLD_LAZY | RTLD_GLOBAL); if (!handle) { - auto currentPath = std::filesystem::current_path(); - auto scipyLibsPath = currentPath.parent_path() / "scipy.libs"; + std::string pathStr(getPath()); + std::filesystem::path currentPath(pathStr); + auto scipyLibsPath = pathStr.parent_path().parent_path() / "scipy.libs"; std::vector> availableLibs; for (const auto &lib : std::filesystem::directory_iterator(scipyLibsPath)) { @@ -152,8 +171,9 @@ void compute_diagonalizing_gates(int n, int lda, } } } else { - auto currentPath = std::filesystem::current_path(); - scipyLibsPath = currentPath.parent_path() / "scipy.libs"; + std::string pathStr(getPath()); + std::filesystem::path currentPath(pathStr); + auto scipyLibsPath = pathStr.parent_path().parent_path() / "scipy.libs"; std::cout << scipyLibsPath << std::endl; for (const auto &lib : std::filesystem::directory_iterator(scipyLibsPath)) { From c9fba49be61b6ee890508afea5c9f216d7caaa4d Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Thu, 21 Mar 2024 18:52:30 -0400 Subject: [PATCH 265/428] quick fix --- pennylane_lightning/core/src/utils/UtilLinearAlg.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index faef41499f..1c91941b74 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -123,7 +123,7 @@ void compute_diagonalizing_gates(int n, int lda, if (!handle) { std::string pathStr(getPath()); std::filesystem::path currentPath(pathStr); - auto scipyLibsPath = pathStr.parent_path().parent_path() / "scipy.libs"; + auto scipyLibsPath = currentPath.parent_path().parent_path() / "scipy.libs"; std::vector> availableLibs; for (const auto &lib : std::filesystem::directory_iterator(scipyLibsPath)) { @@ -173,7 +173,7 @@ void compute_diagonalizing_gates(int n, int lda, } else { std::string pathStr(getPath()); std::filesystem::path currentPath(pathStr); - auto scipyLibsPath = pathStr.parent_path().parent_path() / "scipy.libs"; + auto scipyLibsPath = currentPath.parent_path().parent_path() / "scipy.libs"; std::cout << scipyLibsPath << std::endl; for (const auto &lib : std::filesystem::directory_iterator(scipyLibsPath)) { From 0625ff191877f4034319abc37f9ee449c048cd18 Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Fri, 22 Mar 2024 14:12:00 +0000 Subject: [PATCH 266/428] Update mpitests/test_apply.py --- mpitests/test_apply.py | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/mpitests/test_apply.py b/mpitests/test_apply.py index 87152ee6fb..9c145b07a9 100644 --- a/mpitests/test_apply.py +++ b/mpitests/test_apply.py @@ -219,12 +219,14 @@ def dev_mpi(self, request): ) @pytest.mark.parametrize("Wires", [0, 1, numQubits - 2, numQubits - 1]) def test_apply_operation_single_wire_nonparam(self, tol, operation, Wires, dev_mpi): + print("test_apply_operation_single_wire_nonparam") apply_operation_gates_qnode_nonparam(tol, dev_mpi, operation, Wires) apply_operation_gates_apply_nonparam(tol, dev_mpi, operation, Wires) @pytest.mark.parametrize("operation", [qml.CNOT, qml.SWAP, qml.CY, qml.CZ]) @pytest.mark.parametrize("Wires", [[0, 1], [numQubits - 2, numQubits - 1], [0, numQubits - 1]]) def test_apply_operation_two_wire_nonparam(self, tol, operation, Wires, dev_mpi): + print("test_apply_operation_two_wire_nonparam") apply_operation_gates_qnode_nonparam(tol, dev_mpi, operation, Wires) apply_operation_gates_apply_nonparam(tol, dev_mpi, operation, Wires) @@ -239,6 +241,7 @@ def test_apply_operation_two_wire_nonparam(self, tol, operation, Wires, dev_mpi) ], ) def test_apply_operation_three_wire_nonparam(self, tol, operation, Wires, dev_mpi): + print("test_apply_operation_three_wire_nonparam") apply_operation_gates_qnode_nonparam(tol, dev_mpi, operation, Wires) apply_operation_gates_apply_nonparam(tol, dev_mpi, operation, Wires) @@ -253,6 +256,7 @@ def test_apply_operation_three_wire_nonparam(self, tol, operation, Wires, dev_mp ], ) def test_apply_operation_three_wire_qnode_nonparam(self, tol, operation, Wires, dev_mpi): + print("test_apply_operation_three_wire_qnode_nonparam") apply_operation_gates_qnode_nonparam(tol, dev_mpi, operation, Wires) apply_operation_gates_apply_nonparam(tol, dev_mpi, operation, Wires) @@ -262,6 +266,7 @@ def test_apply_operation_three_wire_qnode_nonparam(self, tol, operation, Wires, def test_apply_operation_1gatequbit_1param_gate_qnode_param( self, tol, operation, par, Wires, dev_mpi ): + print("test_apply_operation_1gatequbit_1param_gate_qnode_param") apply_operation_gates_qnode_param(tol, dev_mpi, operation, par, Wires) apply_operation_gates_apply_param(tol, dev_mpi, operation, par, Wires) @@ -271,6 +276,7 @@ def test_apply_operation_1gatequbit_1param_gate_qnode_param( def test_apply_operation_1gatequbit_3param_gate_qnode_param( self, tol, operation, par, Wires, dev_mpi ): + print("test_apply_operation_1gatequbit_3param_gate_qnode_param") apply_operation_gates_qnode_param(tol, dev_mpi, operation, par, Wires) apply_operation_gates_apply_param(tol, dev_mpi, operation, par, Wires) @@ -280,6 +286,7 @@ def test_apply_operation_1gatequbit_3param_gate_qnode_param( def test_apply_operation_1gatequbit_3param_cgate_qnode_param( self, tol, operation, par, Wires, dev_mpi ): + print("test_apply_operation_1gatequbit_3param_cgate_qnode_param") apply_operation_gates_qnode_param(tol, dev_mpi, operation, par, Wires) apply_operation_gates_apply_param(tol, dev_mpi, operation, par, Wires) @@ -303,6 +310,7 @@ def test_apply_operation_1gatequbit_3param_cgate_qnode_param( def test_apply_operation_2gatequbit_1param_gate_qnode_param( self, tol, operation, par, Wires, dev_mpi ): + print("test_apply_operation_2gatequbit_1param_gate_qnode_param") apply_operation_gates_qnode_param(tol, dev_mpi, operation, par, Wires) apply_operation_gates_apply_param(tol, dev_mpi, operation, par, Wires) @@ -322,6 +330,7 @@ def test_apply_operation_2gatequbit_1param_gate_qnode_param( def test_apply_operation_4gatequbit_1param_gate_qnode_param( self, tol, operation, par, Wires, dev_mpi ): + print("test_apply_operation_4gatequbit_1param_gate_qnode_param") apply_operation_gates_qnode_param(tol, dev_mpi, operation, par, Wires) apply_operation_gates_apply_param(tol, dev_mpi, operation, par, Wires) @@ -329,6 +338,7 @@ def test_apply_operation_4gatequbit_1param_gate_qnode_param( @pytest.mark.parametrize("operation", [qml.BasisState]) @pytest.mark.parametrize("index", range(numQubits)) def test_state_prep(self, tol, operation, index, dev_mpi): + print("test_state_prep") par = np.zeros(numQubits, dtype=int) par[index] = 1 num_wires = numQubits @@ -393,6 +403,7 @@ def circuit(): ], ) def test_qubit_state_prep(self, tol, par, Wires, dev_mpi): + print("test_qubit_state_prep") num_wires = numQubits comm = MPI.COMM_WORLD commSize = comm.Get_size() @@ -429,6 +440,7 @@ def circuit(): assert np.allclose(local_state_vector, local_expected_output_cpu, atol=tol, rtol=0) def test_dev_reset(self, tol, dev_mpi): + print("test_dev_reset") num_wires = numQubits comm = MPI.COMM_WORLD commSize = comm.Get_size() @@ -450,7 +462,7 @@ def test_dev_reset(self, tol, dev_mpi): comm.Scatter(state_vector, local_state_vector, root=0) dev_cpu = qml.device("lightning.qubit", wires=num_wires, c_dtype=c_dtype) - dev_cpu.reset() + dev_cpu._statevector.reset_state() def circuit(): qml.PauliX(wires=[0]) @@ -476,6 +488,7 @@ class TestSparseHamExpval: # pylint: disable=too-few-public-methods,missing-fun @pytest.mark.parametrize("C_DTYPE", [np.complex128, np.complex64]) def test_sparse_hamiltonian_expectation(self, C_DTYPE): + print("test_sparse_hamiltonian_expectation") comm = MPI.COMM_WORLD commSize = comm.Get_size() num_global_wires = commSize.bit_length() - 1 @@ -534,6 +547,7 @@ class TestExpval: ) @pytest.mark.parametrize("wires", [0, 1, 2, numQubits - 3, numQubits - 2, numQubits - 1]) def test_expval_single_wire_no_parameters(self, tol, operation, wires, C_DTYPE): + print("test_expval_single_wire_no_parameters") """Tests that expectation values are properly calculated for single-wire observables without parameters.""" num_wires = numQubits comm = MPI.COMM_WORLD @@ -556,7 +570,7 @@ def circuit(): cpu_qnode = qml.QNode(circuit, dev_cpu) expected_output_cpu = cpu_qnode() - comm.Bcast(expected_output_cpu, root=0) + comm.Bcast(np.array(expected_output_cpu), root=0) mpi_qnode = qml.QNode(circuit, dev_mpi) expected_output_mpi = mpi_qnode() @@ -576,6 +590,7 @@ def circuit(): ], ) def test_expval_multiple_obs(self, obs, tol, C_DTYPE): + print("test_expval_multiple_obs") """Test expval with Hamiltonian""" num_wires = numQubits @@ -621,6 +636,7 @@ def circuit(): ], ) def test_expval_hamiltonian(self, obs, coeffs, tol, C_DTYPE): + print("test_expval_hamiltonian") """Test expval with Hamiltonian""" num_wires = numQubits @@ -640,6 +656,7 @@ def circuit(): assert np.allclose(cpu_qnode(), mpi_qnode(), atol=tol, rtol=0) def test_expval_non_pauli_word_hamiltionian(self, tol): + print("test_expval_non_pauli_word_hamiltionian") """Tests expectation values of non-Pauli word Hamiltonians.""" dev_mpi = qml.device("lightning.gpu", wires=3, mpi=True) dev_cpu = qml.device("lightning.qubit", wires=3) @@ -667,6 +684,7 @@ class TestGenerateSample: @pytest.mark.parametrize("C_DTYPE", [np.complex128, np.complex64]) def test_sample_dimensions(self, C_DTYPE): + print("test_sample_dimensions") """Tests if the samples returned by sample have the correct dimensions """ @@ -698,6 +716,7 @@ def test_sample_dimensions(self, C_DTYPE): @pytest.mark.parametrize("C_DTYPE", [np.complex128, np.complex64]) def test_sample_values(self, tol, C_DTYPE): + print("test_sample_values") """Tests if the samples returned by sample have the correct values """ @@ -717,6 +736,7 @@ def test_sample_values(self, tol, C_DTYPE): @pytest.mark.parametrize("C_DTYPE", [np.complex128, np.complex64]) def test_sample_values_qnode(self, tol, C_DTYPE): + print("test_sample_values_qnode") """Tests if the samples returned by sample have the correct values """ @@ -738,6 +758,7 @@ def circuit(): @pytest.mark.parametrize("C_DTYPE", [np.complex128, np.complex64]) def test_multi_samples_return_correlated_results(self, C_DTYPE): + print("test_multi_samples_return_correlated_results") """Tests if the samples returned by the sample function have the correct dimensions """ @@ -759,6 +780,7 @@ def circuit(): @pytest.mark.parametrize("C_DTYPE", [np.complex128, np.complex64]) def test_paulix_pauliy(self, C_DTYPE, tol=TOL_STOCHASTIC): + print("test_paulix_pauliy") """Test that a tensor product involving PauliX and PauliY works correctly""" num_wires = 3 @@ -801,6 +823,7 @@ def circuit(): @pytest.mark.parametrize("C_DTYPE", [np.complex128, np.complex64]) def test_pauliz_hadamard(self, C_DTYPE, tol=TOL_STOCHASTIC): + print("test_pauliz_hadamard") """Test that a tensor product involving PauliZ and PauliY and hadamard works correctly""" num_wires = 3 @@ -847,6 +870,7 @@ class TestTensorVar: @pytest.mark.parametrize("C_DTYPE", [np.complex128, np.complex64]) def test_paulix_pauliy(self, C_DTYPE, tol=TOL_STOCHASTIC): + print("test_paulix_pauliy") """Test that a tensor product involving PauliX and PauliY works correctly""" num_wires = 3 @@ -881,6 +905,7 @@ def circuit(): @pytest.mark.parametrize("C_DTYPE", [np.complex128, np.complex64]) def test_pauliz_hadamard(self, C_DTYPE, tol=TOL_STOCHASTIC): + print("test_pauliz_hadamard") """Test that a tensor product involving PauliZ and PauliY and hadamard works correctly""" num_wires = 3 dev_gpumpi = qml.device( @@ -974,6 +999,7 @@ def circuit_ansatz(params, wires): ], ) def test_integration(returns): + print("test_integration") """Integration tests that compare to default.qubit for a large circuit containing parametrized operations""" num_wires = numQubits @@ -1021,6 +1047,7 @@ def convert_to_array_default(params): def test_integration_custom_wires(returns): """Integration tests that compare to default.qubit for a large circuit containing parametrized operations and when using custom wire labels""" + print("test_integration_custom_wires") dev_lightning = qml.device("lightning.qubit", wires=custom_wires) dev_gpu = qml.device("lightning.gpu", wires=custom_wires, mpi=True, c_dtype=np.complex128) From e384f9a23a9e1d872b45f88134bde835bf21e04c Mon Sep 17 00:00:00 2001 From: Dev version update bot Date: Fri, 22 Mar 2024 14:12:18 +0000 Subject: [PATCH 267/428] Auto update version --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index 778353eb5b..4089152989 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.36.0-dev16" +__version__ = "0.36.0-dev17" From 876febee1b7bbd34879af43d29ff5337ea12617b Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Fri, 22 Mar 2024 14:40:57 +0000 Subject: [PATCH 268/428] Fix terms in ham serialization. --- mpitests/test_adjoint_jacobian.py | 8 ++------ mpitests/test_apply.py | 27 -------------------------- pennylane_lightning/core/_serialize.py | 7 +++---- tests/test_adjoint_jacobian.py | 8 ++------ 4 files changed, 7 insertions(+), 43 deletions(-) diff --git a/mpitests/test_adjoint_jacobian.py b/mpitests/test_adjoint_jacobian.py index 5272f72062..b5b6f56906 100644 --- a/mpitests/test_adjoint_jacobian.py +++ b/mpitests/test_adjoint_jacobian.py @@ -1396,9 +1396,7 @@ def test_qubit_unitary(dev, n_targets): np.random.seed(1337) par = 2 * np.pi * np.random.rand(n_wires) - U = np.random.rand(2**n_targets, 2**n_targets) + 1j * np.random.rand( - 2**n_targets, 2**n_targets - ) + U = np.random.rand(2**n_targets, 2**n_targets) + 1j * np.random.rand(2**n_targets, 2**n_targets) U, _ = np.linalg.qr(U) init_state = np.random.rand(2**n_wires) + 1j * np.random.rand(2**n_wires) init_state /= np.sqrt(np.dot(np.conj(init_state), init_state)) @@ -1446,9 +1444,7 @@ def test_diff_qubit_unitary(dev, n_targets): np.random.seed(1337) par = 2 * np.pi * np.random.rand(n_wires) - U = np.random.rand(2**n_targets, 2**n_targets) + 1j * np.random.rand( - 2**n_targets, 2**n_targets - ) + U = np.random.rand(2**n_targets, 2**n_targets) + 1j * np.random.rand(2**n_targets, 2**n_targets) U, _ = np.linalg.qr(U) init_state = np.random.rand(2**n_wires) + 1j * np.random.rand(2**n_wires) init_state /= np.sqrt(np.dot(np.conj(init_state), init_state)) diff --git a/mpitests/test_apply.py b/mpitests/test_apply.py index 9c145b07a9..8bffcc6b29 100644 --- a/mpitests/test_apply.py +++ b/mpitests/test_apply.py @@ -219,14 +219,12 @@ def dev_mpi(self, request): ) @pytest.mark.parametrize("Wires", [0, 1, numQubits - 2, numQubits - 1]) def test_apply_operation_single_wire_nonparam(self, tol, operation, Wires, dev_mpi): - print("test_apply_operation_single_wire_nonparam") apply_operation_gates_qnode_nonparam(tol, dev_mpi, operation, Wires) apply_operation_gates_apply_nonparam(tol, dev_mpi, operation, Wires) @pytest.mark.parametrize("operation", [qml.CNOT, qml.SWAP, qml.CY, qml.CZ]) @pytest.mark.parametrize("Wires", [[0, 1], [numQubits - 2, numQubits - 1], [0, numQubits - 1]]) def test_apply_operation_two_wire_nonparam(self, tol, operation, Wires, dev_mpi): - print("test_apply_operation_two_wire_nonparam") apply_operation_gates_qnode_nonparam(tol, dev_mpi, operation, Wires) apply_operation_gates_apply_nonparam(tol, dev_mpi, operation, Wires) @@ -241,7 +239,6 @@ def test_apply_operation_two_wire_nonparam(self, tol, operation, Wires, dev_mpi) ], ) def test_apply_operation_three_wire_nonparam(self, tol, operation, Wires, dev_mpi): - print("test_apply_operation_three_wire_nonparam") apply_operation_gates_qnode_nonparam(tol, dev_mpi, operation, Wires) apply_operation_gates_apply_nonparam(tol, dev_mpi, operation, Wires) @@ -256,7 +253,6 @@ def test_apply_operation_three_wire_nonparam(self, tol, operation, Wires, dev_mp ], ) def test_apply_operation_three_wire_qnode_nonparam(self, tol, operation, Wires, dev_mpi): - print("test_apply_operation_three_wire_qnode_nonparam") apply_operation_gates_qnode_nonparam(tol, dev_mpi, operation, Wires) apply_operation_gates_apply_nonparam(tol, dev_mpi, operation, Wires) @@ -266,7 +262,6 @@ def test_apply_operation_three_wire_qnode_nonparam(self, tol, operation, Wires, def test_apply_operation_1gatequbit_1param_gate_qnode_param( self, tol, operation, par, Wires, dev_mpi ): - print("test_apply_operation_1gatequbit_1param_gate_qnode_param") apply_operation_gates_qnode_param(tol, dev_mpi, operation, par, Wires) apply_operation_gates_apply_param(tol, dev_mpi, operation, par, Wires) @@ -276,7 +271,6 @@ def test_apply_operation_1gatequbit_1param_gate_qnode_param( def test_apply_operation_1gatequbit_3param_gate_qnode_param( self, tol, operation, par, Wires, dev_mpi ): - print("test_apply_operation_1gatequbit_3param_gate_qnode_param") apply_operation_gates_qnode_param(tol, dev_mpi, operation, par, Wires) apply_operation_gates_apply_param(tol, dev_mpi, operation, par, Wires) @@ -286,7 +280,6 @@ def test_apply_operation_1gatequbit_3param_gate_qnode_param( def test_apply_operation_1gatequbit_3param_cgate_qnode_param( self, tol, operation, par, Wires, dev_mpi ): - print("test_apply_operation_1gatequbit_3param_cgate_qnode_param") apply_operation_gates_qnode_param(tol, dev_mpi, operation, par, Wires) apply_operation_gates_apply_param(tol, dev_mpi, operation, par, Wires) @@ -310,7 +303,6 @@ def test_apply_operation_1gatequbit_3param_cgate_qnode_param( def test_apply_operation_2gatequbit_1param_gate_qnode_param( self, tol, operation, par, Wires, dev_mpi ): - print("test_apply_operation_2gatequbit_1param_gate_qnode_param") apply_operation_gates_qnode_param(tol, dev_mpi, operation, par, Wires) apply_operation_gates_apply_param(tol, dev_mpi, operation, par, Wires) @@ -330,7 +322,6 @@ def test_apply_operation_2gatequbit_1param_gate_qnode_param( def test_apply_operation_4gatequbit_1param_gate_qnode_param( self, tol, operation, par, Wires, dev_mpi ): - print("test_apply_operation_4gatequbit_1param_gate_qnode_param") apply_operation_gates_qnode_param(tol, dev_mpi, operation, par, Wires) apply_operation_gates_apply_param(tol, dev_mpi, operation, par, Wires) @@ -338,7 +329,6 @@ def test_apply_operation_4gatequbit_1param_gate_qnode_param( @pytest.mark.parametrize("operation", [qml.BasisState]) @pytest.mark.parametrize("index", range(numQubits)) def test_state_prep(self, tol, operation, index, dev_mpi): - print("test_state_prep") par = np.zeros(numQubits, dtype=int) par[index] = 1 num_wires = numQubits @@ -403,7 +393,6 @@ def circuit(): ], ) def test_qubit_state_prep(self, tol, par, Wires, dev_mpi): - print("test_qubit_state_prep") num_wires = numQubits comm = MPI.COMM_WORLD commSize = comm.Get_size() @@ -440,7 +429,6 @@ def circuit(): assert np.allclose(local_state_vector, local_expected_output_cpu, atol=tol, rtol=0) def test_dev_reset(self, tol, dev_mpi): - print("test_dev_reset") num_wires = numQubits comm = MPI.COMM_WORLD commSize = comm.Get_size() @@ -488,7 +476,6 @@ class TestSparseHamExpval: # pylint: disable=too-few-public-methods,missing-fun @pytest.mark.parametrize("C_DTYPE", [np.complex128, np.complex64]) def test_sparse_hamiltonian_expectation(self, C_DTYPE): - print("test_sparse_hamiltonian_expectation") comm = MPI.COMM_WORLD commSize = comm.Get_size() num_global_wires = commSize.bit_length() - 1 @@ -547,7 +534,6 @@ class TestExpval: ) @pytest.mark.parametrize("wires", [0, 1, 2, numQubits - 3, numQubits - 2, numQubits - 1]) def test_expval_single_wire_no_parameters(self, tol, operation, wires, C_DTYPE): - print("test_expval_single_wire_no_parameters") """Tests that expectation values are properly calculated for single-wire observables without parameters.""" num_wires = numQubits comm = MPI.COMM_WORLD @@ -590,7 +576,6 @@ def circuit(): ], ) def test_expval_multiple_obs(self, obs, tol, C_DTYPE): - print("test_expval_multiple_obs") """Test expval with Hamiltonian""" num_wires = numQubits @@ -636,7 +621,6 @@ def circuit(): ], ) def test_expval_hamiltonian(self, obs, coeffs, tol, C_DTYPE): - print("test_expval_hamiltonian") """Test expval with Hamiltonian""" num_wires = numQubits @@ -656,7 +640,6 @@ def circuit(): assert np.allclose(cpu_qnode(), mpi_qnode(), atol=tol, rtol=0) def test_expval_non_pauli_word_hamiltionian(self, tol): - print("test_expval_non_pauli_word_hamiltionian") """Tests expectation values of non-Pauli word Hamiltonians.""" dev_mpi = qml.device("lightning.gpu", wires=3, mpi=True) dev_cpu = qml.device("lightning.qubit", wires=3) @@ -684,7 +667,6 @@ class TestGenerateSample: @pytest.mark.parametrize("C_DTYPE", [np.complex128, np.complex64]) def test_sample_dimensions(self, C_DTYPE): - print("test_sample_dimensions") """Tests if the samples returned by sample have the correct dimensions """ @@ -716,7 +698,6 @@ def test_sample_dimensions(self, C_DTYPE): @pytest.mark.parametrize("C_DTYPE", [np.complex128, np.complex64]) def test_sample_values(self, tol, C_DTYPE): - print("test_sample_values") """Tests if the samples returned by sample have the correct values """ @@ -736,7 +717,6 @@ def test_sample_values(self, tol, C_DTYPE): @pytest.mark.parametrize("C_DTYPE", [np.complex128, np.complex64]) def test_sample_values_qnode(self, tol, C_DTYPE): - print("test_sample_values_qnode") """Tests if the samples returned by sample have the correct values """ @@ -758,7 +738,6 @@ def circuit(): @pytest.mark.parametrize("C_DTYPE", [np.complex128, np.complex64]) def test_multi_samples_return_correlated_results(self, C_DTYPE): - print("test_multi_samples_return_correlated_results") """Tests if the samples returned by the sample function have the correct dimensions """ @@ -780,7 +759,6 @@ def circuit(): @pytest.mark.parametrize("C_DTYPE", [np.complex128, np.complex64]) def test_paulix_pauliy(self, C_DTYPE, tol=TOL_STOCHASTIC): - print("test_paulix_pauliy") """Test that a tensor product involving PauliX and PauliY works correctly""" num_wires = 3 @@ -823,7 +801,6 @@ def circuit(): @pytest.mark.parametrize("C_DTYPE", [np.complex128, np.complex64]) def test_pauliz_hadamard(self, C_DTYPE, tol=TOL_STOCHASTIC): - print("test_pauliz_hadamard") """Test that a tensor product involving PauliZ and PauliY and hadamard works correctly""" num_wires = 3 @@ -870,7 +847,6 @@ class TestTensorVar: @pytest.mark.parametrize("C_DTYPE", [np.complex128, np.complex64]) def test_paulix_pauliy(self, C_DTYPE, tol=TOL_STOCHASTIC): - print("test_paulix_pauliy") """Test that a tensor product involving PauliX and PauliY works correctly""" num_wires = 3 @@ -905,7 +881,6 @@ def circuit(): @pytest.mark.parametrize("C_DTYPE", [np.complex128, np.complex64]) def test_pauliz_hadamard(self, C_DTYPE, tol=TOL_STOCHASTIC): - print("test_pauliz_hadamard") """Test that a tensor product involving PauliZ and PauliY and hadamard works correctly""" num_wires = 3 dev_gpumpi = qml.device( @@ -999,7 +974,6 @@ def circuit_ansatz(params, wires): ], ) def test_integration(returns): - print("test_integration") """Integration tests that compare to default.qubit for a large circuit containing parametrized operations""" num_wires = numQubits @@ -1047,7 +1021,6 @@ def convert_to_array_default(params): def test_integration_custom_wires(returns): """Integration tests that compare to default.qubit for a large circuit containing parametrized operations and when using custom wire labels""" - print("test_integration_custom_wires") dev_lightning = qml.device("lightning.qubit", wires=custom_wires) dev_gpu = qml.device("lightning.gpu", wires=custom_wires, mpi=True, c_dtype=np.complex128) diff --git a/pennylane_lightning/core/_serialize.py b/pennylane_lightning/core/_serialize.py index 50501dc8c7..8e65868c7d 100644 --- a/pennylane_lightning/core/_serialize.py +++ b/pennylane_lightning/core/_serialize.py @@ -14,7 +14,7 @@ r""" Helper functions for serializing quantum tapes. """ -from typing import List, Tuple +from typing import List, Sequence, Tuple import numpy as np from pennylane import ( @@ -199,6 +199,7 @@ def _hamiltonian(self, observable, wires_map: dict = None): coeffs, ops = observable.terms() coeffs = np.array(unwrap(coeffs)).astype(self.rtype) terms = [self._ob(t, wires_map) for t in ops] + terms = [t[0] if isinstance(t, Sequence) and len(t) == 1 else t for t in terms] if self.split_obs: return [self.hamiltonian_obs([c], [t]) for (c, t) in zip(coeffs, terms)] @@ -309,9 +310,7 @@ def serialize_observables(self, tape: QuantumTape, wires_map: dict = None) -> Li offset_indices.append(offset_indices[-1] + 1) return serialized_obs, offset_indices - def serialize_ops( - self, tape: QuantumTape, wires_map: dict = None - ) -> Tuple[ + def serialize_ops(self, tape: QuantumTape, wires_map: dict = None) -> Tuple[ List[List[str]], List[np.ndarray], List[List[int]], diff --git a/tests/test_adjoint_jacobian.py b/tests/test_adjoint_jacobian.py index 94226696ee..5e246b9f10 100644 --- a/tests/test_adjoint_jacobian.py +++ b/tests/test_adjoint_jacobian.py @@ -1571,9 +1571,7 @@ def test_qubit_unitary(n_targets): init_state = np.random.rand(2**n_wires) + 1j * np.random.rand(2**n_wires) init_state /= np.sqrt(np.dot(np.conj(init_state), init_state)) init_state = np.array(init_state, requires_grad=False) - U = np.random.rand(2**n_targets, 2**n_targets) + 1j * np.random.rand( - 2**n_targets, 2**n_targets - ) + U = np.random.rand(2**n_targets, 2**n_targets) + 1j * np.random.rand(2**n_targets, 2**n_targets) U, _ = np.linalg.qr(U) U = np.array(U, requires_grad=False) @@ -1615,9 +1613,7 @@ def test_diff_qubit_unitary(n_targets): init_state = np.random.rand(2**n_wires) + 1j * np.random.rand(2**n_wires) init_state /= np.sqrt(np.dot(np.conj(init_state), init_state)) init_state = np.array(init_state, requires_grad=False) - U = np.random.rand(2**n_targets, 2**n_targets) + 1j * np.random.rand( - 2**n_targets, 2**n_targets - ) + U = np.random.rand(2**n_targets, 2**n_targets) + 1j * np.random.rand(2**n_targets, 2**n_targets) U, _ = np.linalg.qr(U) U = np.array(U, requires_grad=False) From eb132bc57d828d0677a3c04bb86b82932cfb2ec6 Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Fri, 22 Mar 2024 14:47:39 +0000 Subject: [PATCH 269/428] Use requirements-dev.txt to install black/isort. --- .github/workflows/format.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index bb42e3eb47..eb2b3d30c8 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -19,13 +19,13 @@ jobs: with: python-version: '3.11' - - name: Install dependencies - run: - python -m pip install click==8.0.4 black==23.7.0 isort==5.13.2 - - name: Checkout PennyLane-Lightning uses: actions/checkout@v3 + - name: Install dependencies + run: + python -m pip install -r requirements-dev.txt + - name: Run isort & black --check run: make format-python check=1 verbose=1 From 28cabce8399d755ed75791d75f9a15e648072ea7 Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Fri, 22 Mar 2024 15:10:16 +0000 Subject: [PATCH 270/428] Mark test_single_return_value flaky. Add comment on split_obs. --- pennylane_lightning/core/_serialize.py | 2 ++ tests/lightning_qubit/test_measurements_class.py | 1 + 2 files changed, 3 insertions(+) diff --git a/pennylane_lightning/core/_serialize.py b/pennylane_lightning/core/_serialize.py index 8e65868c7d..1f5bef250e 100644 --- a/pennylane_lightning/core/_serialize.py +++ b/pennylane_lightning/core/_serialize.py @@ -199,6 +199,8 @@ def _hamiltonian(self, observable, wires_map: dict = None): coeffs, ops = observable.terms() coeffs = np.array(unwrap(coeffs)).astype(self.rtype) terms = [self._ob(t, wires_map) for t in ops] + # TODO: This is in case `_hamiltonian` is called recursively which would cause a list + # to be passed where `_ob` expects an observable. terms = [t[0] if isinstance(t, Sequence) and len(t) == 1 else t for t in terms] if self.split_obs: diff --git a/tests/lightning_qubit/test_measurements_class.py b/tests/lightning_qubit/test_measurements_class.py index 41e5be61e4..bd070629f8 100644 --- a/tests/lightning_qubit/test_measurements_class.py +++ b/tests/lightning_qubit/test_measurements_class.py @@ -418,6 +418,7 @@ def calculate_reference(tape, lightning_sv): m = LightningMeasurements(statevector) return m.measure_final_state(tape) + @flaky(max_runs=5) @pytest.mark.parametrize("measurement", [qml.expval, qml.probs, qml.var]) @pytest.mark.parametrize( "observable", From 3420410143ae426437d96830ad7ed69813e06991 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Fri, 22 Mar 2024 12:57:58 -0400 Subject: [PATCH 271/428] add sharedLibLoader wrapper class --- .../core/src/utils/SharedLibLoader.hpp | 88 +++++++++++++ .../core/src/utils/UtilLinearAlg.hpp | 119 +++++------------- 2 files changed, 121 insertions(+), 86 deletions(-) create mode 100644 pennylane_lightning/core/src/utils/SharedLibLoader.hpp diff --git a/pennylane_lightning/core/src/utils/SharedLibLoader.hpp b/pennylane_lightning/core/src/utils/SharedLibLoader.hpp new file mode 100644 index 0000000000..80689e65fb --- /dev/null +++ b/pennylane_lightning/core/src/utils/SharedLibLoader.hpp @@ -0,0 +1,88 @@ +// Copyright 2018-2024 Xanadu Quantum Technologies Inc. + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 + +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @file + * Dynamic shared library functions API wrapper. + */ +#include + +#if defined(__APPLE__) || defined(__linux__) +#include +#elif defined(_MSC_VER) +#include +#endif + +#include "Error.hpp" + +namespace Pennylane::Util { +/** + * Dynamic shared library loading wrapper class + */ +// Adapted from Catalyst +// (https://github.com/PennyLaneAI/catalyst/blob/f016a31f69d1b8a84bc9612af1bc64f0575506e9/runtime/lib/capi/ExecutionContext.hpp#L75) +class SharedLibLoader final { + private: +#if defined(__APPLE__) || defined(__linux__) + void *handle_{nullptr}; +#elif defined(_MSC_VER) + HMODULE handle_{nullptr}; +#endif + + public: + SharedLibLoader(); + explicit SharedLibLoader(const std::string &filename) { +#if defined(__APPLE__) + auto rtld_flags = RTLD_LAZY; +#elif defined(__linux__) + auto rtld_flags = RTLD_LAZY | RTLD_NODELETE; +#endif + +#if defined(__APPLE__) || defined(__linux__) + handle_ = dlopen(filename.c_str(), rtld_flags); + // This allows users to use pre-installed LAPACK package + if (filename != "liblapack.so") { + PL_ABORT_IF(!_handle_, dlerror()); + } +#elif defined(_MSC_VER) + handle_ = LoadLibrary(filename.c_str()); + if (filename != "libopenblas.dll") { + PL_ABORT_IF(!handle_, std::to_string(GetLastError())); + } +#endif + } + + ~SharedLibLoader() { +#if defined(__APPLE__) || defined(__linux__) + dlclose(handle_); +#elif defined(_MSC_VER) + FreeLibrary(handle_); +#endif + } + + void *getHandle() { return handle_; } + + auto getSymbol(const std::string &symbol) { +#if defined(__APPLE__) || defined(__linux__) + auto sym = dlsym(handle_, symbol.c_str()); + PL_ABORT_IF(!sym, dlerror()); +#elif defined(_MSC_VER) + auto sym = GetProcAddress(handle_, symbol.c_str()); + PL_ABORT_IF(!handle_, std::to_string(GetLastError())); +#endif + return sym; + } +}; + +} // namespace Pennylane::Util \ No newline at end of file diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index 1c91941b74..02c852ee35 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -48,14 +49,14 @@ typedef void (*cheevPtr)(const char *, const char *, const int *, std::unordered_map priority_lib = { {"stdc", 0}, {"gcc", 1}, {"quadmath", 2}, {"gfortran", 3}, {"openblas", 4}}; + #ifdef __linux__ -const char *getPath() { +std::string getPath() { Dl_info dl_info; - if (dladdr((const void *)getPath, &dl_info) != 0) { - return dl_info.dli_fname; - } else { - return nullptr; - } + auto flag = dladdr((const void *)getPath, &dl_info); + PL_ABORT_IF(!flag, "Can't get the path to the shared library."); + std::string path(dl_info.dli_fname); + return path; } #elif defined(_MSC_VER) std::string getPath() { @@ -101,30 +102,29 @@ void compute_diagonalizing_gates(int n, int lda, } } #ifdef __APPLE__ - std::vector handles; - void *handle = - dlopen("/System/Library/Frameworks/Accelerate.framework/Versions/" - "Current/Frameworks/vecLib.framework/libLAPACK.dylib", - RTLD_LAZY | RTLD_GLOBAL); - if (!handle) { - handle = dlopen("/usr/local/opt/lapack/lib/liblapack.dylib", - RTLD_LAZY | RTLD_GLOBAL); - } - if (!handle) { - fprintf(stderr, "%s\n", dlerror()); - } - handles.push_back(handle); -#elif defined(__linux__) - std::vector handles; - void *handle; + const std::string libName = + "/System/Library/Frameworks/Accelerate.framework/Versions/Current/" + "Frameworks/vecLib.framework/libLAPACK.dylib"; + std::shared_ptr blasLib = + std::make_shared(libName); +#else +#if defined(__linux__) + const std::string defaultLibName = "liblapack.so"; +#else + const std::string defaultLibName = "libopenblas.dll"; +#endif + std::shared_ptr blasLib = + std::make_shared(defaultLibName); - handle = dlopen("liblapack.so", RTLD_LAZY | RTLD_GLOBAL); + std::vector> blasLibs; + if (!blasLib->getHandle()) { + const std::string currentPathStr(getPath()); + const std::filesystem::path currentPath(pathStr); + const auto scipyLibsPath = + currentPath.parent_path().parent_path() / "scipy.libs"; - if (!handle) { - std::string pathStr(getPath()); - std::filesystem::path currentPath(pathStr); - auto scipyLibsPath = currentPath.parent_path().parent_path() / "scipy.libs"; std::vector> availableLibs; + for (const auto &lib : std::filesystem::directory_iterator(scipyLibsPath)) { if (lib.is_regular_file()) { @@ -145,48 +145,12 @@ void compute_diagonalizing_gates(int n, int lda, for (const auto &lib : availableLibs) { auto libPath = scipyLibsPath / lib.first.c_str(); - handle = dlopen(libPath.c_str(), RTLD_LAZY | RTLD_GLOBAL); - if (!handle) { - fprintf(stderr, "%s\n", dlerror()); - } - handles.push_back(handle); + const std::string libPathStr = libPath.c_str(); + blasLibs.emplace_back( + std::make_shared(libPathStr)); } + blasLib = blasLibs.back(); } -#elif defined(_MSC_VER) - const char *PythonSitePackagePath = std::getenv("PYTHON_SITE_PACKAGES"); - std::string openblasLib; - std::filesystem::path scipyLibsPath; - if (PythonSitePackagePath != nullptr) { - std::filesystem::path tmpPath(PythonSitePackagePath); - scipyLibsPath = tmpPath; - scipyLibsPath = scipyLibsPath / "scipy.libs"; - std::cout << scipyLibsPath << std::endl; - for (const auto &lib : - std::filesystem::directory_iterator(scipyLibsPath)) { - if (lib.is_regular_file()) { - std::string libname_str = lib.path().filename().string(); - if (libname_str.find("openblas") != std::string::npos) { - openblasLib = libname_str; - } - } - } - } else { - std::string pathStr(getPath()); - std::filesystem::path currentPath(pathStr); - auto scipyLibsPath = currentPath.parent_path().parent_path() / "scipy.libs"; - std::cout << scipyLibsPath << std::endl; - for (const auto &lib : - std::filesystem::directory_iterator(scipyLibsPath)) { - if (lib.is_regular_file()) { - std::string libname_str = lib.path().filename().string(); - if (libname_str.find("openblas") != std::string::npos) { - openblasLib = libname_str; - } - } - } - } - auto libPath = scipyLibsPath / openblasLib.c_str(); - HMODULE handle = LoadLibrary(libPath.string().c_str()); #endif char jobz = 'V'; // Enable both eigenvalues and eigenvectors computation @@ -197,12 +161,8 @@ void compute_diagonalizing_gates(int n, int lda, int info; if constexpr (std::is_same::value) { -#if defined(__APPLE__) || defined(__linux__) - cheevPtr cheev = reinterpret_cast(dlsym(handle, "cheev_")); -#elif defined(_MSC_VER) cheevPtr cheev = - reinterpret_cast(GetProcAddress(handle, "cheev_")); -#endif + reinterpret_cast(blasLib->getSymbol("cheev_")); // Query optimal workspace size cheev(&jobz, &uplo, &n, ah.data(), &lda, eigenVals.data(), work_query.data(), &lwork, rwork.data(), &info); @@ -213,12 +173,8 @@ void compute_diagonalizing_gates(int n, int lda, cheev(&jobz, &uplo, &n, ah.data(), &lda, eigenVals.data(), work_optimal.data(), &lwork, rwork.data(), &info); } else { -#if defined(__APPLE__) || defined(__linux__) - zheevPtr zheev = reinterpret_cast(dlsym(handle, "zheev_")); -#elif defined(_MSC_VER) zheevPtr zheev = - reinterpret_cast(GetProcAddress(handle, "zheev_")); -#endif + reinterpret_cast(blasLib->getSymbol("zheev_")); // Query optimal workspace size zheev(&jobz, &uplo, &n, ah.data(), &lda, eigenVals.data(), work_query.data(), &lwork, rwork.data(), &info); @@ -230,15 +186,6 @@ void compute_diagonalizing_gates(int n, int lda, work_optimal.data(), &lwork, rwork.data(), &info); } -#if defined(__APPLE__) || defined(__linux__) - dlclose(handle); - for (auto handle : handles) { - dlclose(handle); - } -#elif defined(_MSC_VER) - FreeLibrary(handle); -#endif - std::transform(ah.begin(), ah.end(), unitary.begin(), [](std::complex value) { return std::complex{value.real(), -value.imag()}; From e97f886375f873a0bd1fcbefa0eb3500b62201ba Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Fri, 22 Mar 2024 13:08:04 -0400 Subject: [PATCH 272/428] fix typo --- pennylane_lightning/core/src/utils/SharedLibLoader.hpp | 2 +- pennylane_lightning/core/src/utils/UtilLinearAlg.hpp | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/pennylane_lightning/core/src/utils/SharedLibLoader.hpp b/pennylane_lightning/core/src/utils/SharedLibLoader.hpp index 80689e65fb..90fa439a89 100644 --- a/pennylane_lightning/core/src/utils/SharedLibLoader.hpp +++ b/pennylane_lightning/core/src/utils/SharedLibLoader.hpp @@ -53,7 +53,7 @@ class SharedLibLoader final { handle_ = dlopen(filename.c_str(), rtld_flags); // This allows users to use pre-installed LAPACK package if (filename != "liblapack.so") { - PL_ABORT_IF(!_handle_, dlerror()); + PL_ABORT_IF(!handle_, dlerror()); } #elif defined(_MSC_VER) handle_ = LoadLibrary(filename.c_str()); diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index 02c852ee35..e9879982be 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -27,16 +27,15 @@ #include #include -#include - #if defined(__APPLE__) || defined(__linux__) #include #elif defined(_MSC_VER) #include -#include #include #endif +#include "SharedLibLoader.hpp" + /// @cond DEV namespace { // LAPACK routine for complex Hermitian eigensystems From 2923ec4847109f61e31fd4eeb76595702a6b7e23 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Fri, 22 Mar 2024 14:24:38 -0400 Subject: [PATCH 273/428] fix typo --- pennylane_lightning/core/src/utils/UtilLinearAlg.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index e9879982be..eec1290c8a 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -118,7 +118,7 @@ void compute_diagonalizing_gates(int n, int lda, std::vector> blasLibs; if (!blasLib->getHandle()) { const std::string currentPathStr(getPath()); - const std::filesystem::path currentPath(pathStr); + const std::filesystem::path currentPath(currentPathStr); const auto scipyLibsPath = currentPath.parent_path().parent_path() / "scipy.libs"; From 6b16395a8ebbe1b4c08311ab089326545f7d80cd Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Fri, 22 Mar 2024 14:41:20 -0400 Subject: [PATCH 274/428] remove vcpkg dependence --- .github/workflows/tests_windows.yml | 67 +++---------------- .../core/src/utils/SharedLibLoader.hpp | 8 +-- .../core/src/utils/UtilLinearAlg.hpp | 4 +- 3 files changed, 15 insertions(+), 64 deletions(-) diff --git a/.github/workflows/tests_windows.yml b/.github/workflows/tests_windows.yml index 777cd3cd0c..21481c6c25 100644 --- a/.github/workflows/tests_windows.yml +++ b/.github/workflows/tests_windows.yml @@ -93,44 +93,7 @@ jobs: cmake --build ./Build --config Debug --verbose cmake --install ./Build --config Debug --verbose - build_dependencies_vcpkg: - strategy: - fail-fast: false - matrix: - os: [windows-latest] - timeout-minutes: 30 - name: vcpkg dependencies - runs-on: ${{ matrix.os }} - - steps: - - name: Cache installation directories - id: vcpkg-cache - uses: actions/cache@v4 - with: - path: D:\a\install_dir\vcpkg - key: ${{ matrix.os }}-vcpkg - - - name: Clone vcpkg - if: steps.vcpkg-cache.outputs.cache-hit != 'true' - run: | - mkdir -p D:\a\install_dir - cd D:\a\install_dir\ - git clone --quiet --recursive https://github.com/microsoft/vcpkg.git - - - name: Install dependencies - if: steps.vcpkg-cache.outputs.cache-hit != 'true' - run: | - python -m pip install cmake build ninja - - - name: Build Lapack library - if: steps.vcpkg-cache.outputs.cache-hit != 'true' - run: | - cd D:\a\install_dir\vcpkg - .\bootstrap-vcpkg.bat - vcpkg install lapack - cpptests: - needs: [build_dependencies_vcpkg] if: ${{ !contains(fromJSON('["schedule", "workflow_dispatch"]'), github.event_name) }} timeout-minutes: 30 name: C++ tests (Windows) @@ -142,13 +105,10 @@ jobs: steps: - name: Checkout PennyLane-Lightning uses: actions/checkout@v3 - - - name: Restoring cached vcpkg - id: vcpkg-cache - uses: actions/cache@v4 - with: - path: D:\a\install_dir\vcpkg - key: ${{ matrix.os }}-vcpkg + + - name: Install dependencies + run: | + python -m pip install cmake build ninja scipy - name: Setup OpenCppCoverage and add to PATH run: | @@ -159,7 +119,7 @@ jobs: id: get_site_packages shell: pwsh run: | - $pythonSitePackages = python -c "import site; print(site.getsitepackages()[0])" + pythonSitePackages = $(python -c "import site; print(site.getsitepackages()[0])") echo "PYTHON_SITE_PACKAGES=$pythonSitePackages" >> $GITHUB_ENV - name: Build and run unit tests for code coverage @@ -170,7 +130,6 @@ jobs: -DENABLE_PYTHON=OFF ` -DENABLE_GATE_DISPATCHER=OFF ` -DPL_BACKEND=${{ matrix.pl_backend }} ` - -DCMAKE_TOOLCHAIN_FILE=D:\a\install_dir\vcpkg\scripts\buildsystems\vcpkg.cmake ` -DENABLE_WARNINGS=OFF cmake --build .\Build --config RelWithDebInfo mkdir -p .\Build\tests\results @@ -203,7 +162,7 @@ jobs: cpptestswithkokkos: if: ${{ !contains(fromJSON('["schedule", "workflow_dispatch"]'), github.event_name) }} - needs: [build_dependencies_kokkos, build_dependencies_vcpkg, win-set-matrix-x86] + needs: [build_dependencies_kokkos, win-set-matrix-x86] strategy: matrix: os: [windows-latest] @@ -223,13 +182,10 @@ jobs: path: D:\a\install_dir\${{ matrix.exec_model }} key: ${{ matrix.os }}-kokkos${{ matrix.kokkos_version }}-${{ matrix.exec_model }}-Debug - - name: Restoring cached vcpkg - id: vcpkg-cache - uses: actions/cache@v4 - with: - path: D:\a\install_dir\vcpkg - key: ${{ matrix.os }}-vcpkg - + - name: Install dependencies + run: | + python -m pip install cmake build ninja scipy + - name: Checkout PennyLane-Lightning uses: actions/checkout@v3 @@ -252,7 +208,7 @@ jobs: id: get_site_packages shell: pwsh run: | - $pythonSitePackages = python -c "import site; print(site.getsitepackages()[0])" + pythonSitePackages = $(python -c "import site; print(site.getsitepackages()[0])") echo "PYTHON_SITE_PACKAGES=$pythonSitePackages" >> $GITHUB_ENV - name: Build and run unit tests for code coverage @@ -264,7 +220,6 @@ jobs: -DENABLE_PYTHON=OFF ` -DENABLE_GATE_DISPATCHER=OFF ` -DCMAKE_PREFIX_PATH=D:\a\pennylane-lightning\pennylane-lightning\Kokkos ` - -DCMAKE_TOOLCHAIN_FILE=D:\a\install_dir\vcpkg\scripts\buildsystems\vcpkg.cmake ` -DENABLE_OPENMP=OFF ` -DPL_BACKEND=${{ matrix.pl_backend }} ` -DENABLE_WARNINGS=OFF -T clangcl diff --git a/pennylane_lightning/core/src/utils/SharedLibLoader.hpp b/pennylane_lightning/core/src/utils/SharedLibLoader.hpp index 90fa439a89..26bbab644e 100644 --- a/pennylane_lightning/core/src/utils/SharedLibLoader.hpp +++ b/pennylane_lightning/core/src/utils/SharedLibLoader.hpp @@ -52,14 +52,10 @@ class SharedLibLoader final { #if defined(__APPLE__) || defined(__linux__) handle_ = dlopen(filename.c_str(), rtld_flags); // This allows users to use pre-installed LAPACK package - if (filename != "liblapack.so") { - PL_ABORT_IF(!handle_, dlerror()); - } + PL_ABORT_IF(!handle_, dlerror()); #elif defined(_MSC_VER) handle_ = LoadLibrary(filename.c_str()); - if (filename != "libopenblas.dll") { - PL_ABORT_IF(!handle_, std::to_string(GetLastError())); - } + PL_ABORT_IF(!handle_, std::to_string(GetLastError())); #endif } diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index eec1290c8a..535bafcb6d 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -128,7 +128,7 @@ void compute_diagonalizing_gates(int n, int lda, std::filesystem::directory_iterator(scipyLibsPath)) { if (lib.is_regular_file()) { for (const auto &iter : priority_lib) { - std::string libname_str = lib.path().filename(); + std::string libname_str = lib.path().filename().string(); if (libname_str.find(iter.first) != std::string ::npos) { availableLibs.emplace_back( std::make_pair(libname_str, iter.second)); @@ -144,7 +144,7 @@ void compute_diagonalizing_gates(int n, int lda, for (const auto &lib : availableLibs) { auto libPath = scipyLibsPath / lib.first.c_str(); - const std::string libPathStr = libPath.c_str(); + const std::string libPathStr = libPath.string().c_str(); blasLibs.emplace_back( std::make_shared(libPathStr)); } From d677529e30ef20e25b0a62770a5a531e5d9eeefd Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Fri, 22 Mar 2024 14:49:27 -0400 Subject: [PATCH 275/428] windows workflow test --- .github/workflows/tests_windows.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests_windows.yml b/.github/workflows/tests_windows.yml index 21481c6c25..77fc98b8a3 100644 --- a/.github/workflows/tests_windows.yml +++ b/.github/workflows/tests_windows.yml @@ -119,7 +119,8 @@ jobs: id: get_site_packages shell: pwsh run: | - pythonSitePackages = $(python -c "import site; print(site.getsitepackages()[0])") + python -c "import site; print(site.getsitepackages()[0]) + $pythonSitePackages = $(python -c "import site; print(site.getsitepackages()[0])") echo "PYTHON_SITE_PACKAGES=$pythonSitePackages" >> $GITHUB_ENV - name: Build and run unit tests for code coverage @@ -208,7 +209,7 @@ jobs: id: get_site_packages shell: pwsh run: | - pythonSitePackages = $(python -c "import site; print(site.getsitepackages()[0])") + $pythonSitePackages = $(python -c "import site; print(site.getsitepackages()[0])") echo "PYTHON_SITE_PACKAGES=$pythonSitePackages" >> $GITHUB_ENV - name: Build and run unit tests for code coverage From f22d73d1295d5a6bcf4a1afb73866db4e97388a2 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Fri, 22 Mar 2024 14:52:18 -0400 Subject: [PATCH 276/428] windows yaml test --- .github/workflows/tests_windows.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/tests_windows.yml b/.github/workflows/tests_windows.yml index 77fc98b8a3..352245065d 100644 --- a/.github/workflows/tests_windows.yml +++ b/.github/workflows/tests_windows.yml @@ -116,8 +116,6 @@ jobs: echo "C:\Program Files\OpenCppCoverage" >> $env:GITHUB_PATH - name: Get site-packages directory - id: get_site_packages - shell: pwsh run: | python -c "import site; print(site.getsitepackages()[0]) $pythonSitePackages = $(python -c "import site; print(site.getsitepackages()[0])") From 59b291949ee8b8e2415e3b947fc7f820ccb978e5 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Fri, 22 Mar 2024 14:57:33 -0400 Subject: [PATCH 277/428] windows yaml test --- .github/workflows/tests_windows.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests_windows.yml b/.github/workflows/tests_windows.yml index 352245065d..89bfee038c 100644 --- a/.github/workflows/tests_windows.yml +++ b/.github/workflows/tests_windows.yml @@ -117,7 +117,7 @@ jobs: - name: Get site-packages directory run: | - python -c "import site; print(site.getsitepackages()[0]) + python -c "import site; print(site.getsitepackages()[0])" $pythonSitePackages = $(python -c "import site; print(site.getsitepackages()[0])") echo "PYTHON_SITE_PACKAGES=$pythonSitePackages" >> $GITHUB_ENV From 0324ad807e04b75e9122c4cfb9ecf6b75e2664ff Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Fri, 22 Mar 2024 15:01:19 -0400 Subject: [PATCH 278/428] echo pythonSitePackages --- .github/workflows/tests_windows.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests_windows.yml b/.github/workflows/tests_windows.yml index 89bfee038c..c609d26dea 100644 --- a/.github/workflows/tests_windows.yml +++ b/.github/workflows/tests_windows.yml @@ -117,8 +117,8 @@ jobs: - name: Get site-packages directory run: | - python -c "import site; print(site.getsitepackages()[0])" - $pythonSitePackages = $(python -c "import site; print(site.getsitepackages()[0])") + pythonSitePackages = $(python -c "import site; print(site.getsitepackages()[0])") + echo $pythonSitePackages echo "PYTHON_SITE_PACKAGES=$pythonSitePackages" >> $GITHUB_ENV - name: Build and run unit tests for code coverage From 337a5dd84b027898381051674f48737f9e74238d Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Fri, 22 Mar 2024 15:01:21 -0400 Subject: [PATCH 279/428] Apply suggestions from code review Co-authored-by: Ali Asadi <10773383+maliasadi@users.noreply.github.com> Co-authored-by: Vincent Michaud-Rioux --- pennylane_lightning/core/_serialize.py | 2 - pennylane_lightning/core/lightning_base.py | 2 +- tests/test_serialize.py | 110 ++++++++++----------- 3 files changed, 54 insertions(+), 60 deletions(-) diff --git a/pennylane_lightning/core/_serialize.py b/pennylane_lightning/core/_serialize.py index 1f5bef250e..ad2abb5598 100644 --- a/pennylane_lightning/core/_serialize.py +++ b/pennylane_lightning/core/_serialize.py @@ -184,14 +184,12 @@ def _named_obs(self, observable, wires_map: dict = None): def _hermitian_ob(self, observable, wires_map: dict = None): """Serializes a Hermitian observable""" - assert not isinstance(observable, (Tensor, Prod)) wires = [wires_map[w] for w in observable.wires] if wires_map else observable.wires.tolist() return self.hermitian_obs(matrix(observable).ravel().astype(self.ctype), wires) def _tensor_ob(self, observable, wires_map: dict = None): """Serialize a tensor observable""" - assert isinstance(observable, (Tensor, Prod)) obs = observable.obs if isinstance(observable, Tensor) else observable.operands return self.tensor_obs([self._ob(o, wires_map) for o in obs]) diff --git a/pennylane_lightning/core/lightning_base.py b/pennylane_lightning/core/lightning_base.py index 9d28b87e6e..c4f72293a0 100644 --- a/pennylane_lightning/core/lightning_base.py +++ b/pennylane_lightning/core/lightning_base.py @@ -313,7 +313,7 @@ def _assert_adjdiff_no_projectors(observable): Projectors Args: - observable (.Operator): Observable to check + observable (~pennylane.operation.Operator): Observable to check Raises: ~pennylane.QuantumFunctionError: if a ``Projector`` is found. diff --git a/tests/test_serialize.py b/tests/test_serialize.py index ed48947847..7b29292a24 100644 --- a/tests/test_serialize.py +++ b/tests/test_serialize.py @@ -245,37 +245,24 @@ def test_hamiltonian_return(self, use_csingle, wires_map): tape, wires_map ) - if qml.operation.active_new_opmath(): - s_expected = hamiltonian_obs( - np.array([0.3, 0.5, 0.4], dtype=r_dtype), - [ - tensor_prod_obs( - [ - hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), - named_obs("PauliY", [2]), - ] - ), - hamiltonian_obs( - np.array([1], dtype=r_dtype), - [tensor_prod_obs([named_obs("PauliY", [2]), named_obs("PauliX", [0])])], - ), - hermitian_obs(np.ones(64, dtype=c_dtype), [0, 1, 2]), - ], - ) - else: - s_expected = hamiltonian_obs( - np.array([0.3, 0.5, 0.4], dtype=r_dtype), - [ - tensor_prod_obs( - [ - hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), - named_obs("PauliY", [2]), - ] - ), - tensor_prod_obs([named_obs("PauliX", [0]), named_obs("PauliY", [2])]), - hermitian_obs(np.ones(64, dtype=c_dtype), [0, 1, 2]), - ], - ) + s_expected = hamiltonian_obs( + np.array([0.3, 0.5, 0.4], dtype=r_dtype), + [ + tensor_prod_obs( + [ + hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), + named_obs("PauliY", [2]), + ] + ), + hamiltonian_obs( + np.array([1], dtype=r_dtype), + [tensor_prod_obs([named_obs("PauliY", [2]), named_obs("PauliX", [0])])], + ) + if qml.operation.active_new_opmath() + else tensor_prod_obs([named_obs("PauliX", [0]), named_obs("PauliY", [2])]), + hermitian_obs(np.ones(64, dtype=c_dtype), [0, 1, 2]), + ], + ) assert s[0] == s_expected @@ -405,32 +392,41 @@ def test_hamiltonian_mix_return(self, use_csingle, wires_map): tape, wires_map ) - if qml.operation.active_new_opmath(): - s_expected1 = hamiltonian_obs( - np.array([0.3, 0.5, 0.4], dtype=r_dtype), - [ - tensor_prod_obs( - [ - hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), - named_obs("PauliY", [2]), - ] - ), - hamiltonian_obs( - np.array([1], dtype=r_dtype), - [tensor_prod_obs([named_obs("PauliY", [2]), named_obs("PauliX", [0])])], - ), - hermitian_obs(np.ones(64, dtype=c_dtype), [0, 1, 2]), - ], - ) - s_expected2 = hamiltonian_obs( - np.array([0.7, 0.3], dtype=r_dtype), - [ - tensor_prod_obs( - [ - named_obs("PauliX", [0]), - hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [1, 2]), - ] - ), + s_expected1 = hamiltonian_obs( + np.array([0.3, 0.5, 0.4], dtype=r_dtype), + [ + tensor_prod_obs( + [ + hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), + named_obs("PauliY", [2]), + ] + ), + hamiltonian_obs( + np.array([1], dtype=r_dtype), + [tensor_prod_obs([named_obs("PauliY", [2]), named_obs("PauliX", [0])])], + ) + if qml.operation.active_new_opmath() + else tensor_prod_obs([named_obs("PauliX", [0]), named_obs("PauliY", [2])]), + hermitian_obs(np.ones(64, dtype=c_dtype), [0, 1, 2]), + ], + ) + s_expected2 = hamiltonian_obs( + np.array([0.7, 0.3], dtype=r_dtype), + [ + tensor_prod_obs( + [ + named_obs("PauliX", [0]), + hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [1, 2]), + ] + ), + hamiltonian_obs( + np.array([1], dtype=r_dtype), + [tensor_prod_obs([named_obs("PauliX", [2]), named_obs("PauliY", [0])])], + ) + if qml.operation.active_new_opmath() + else tensor_prod_obs([named_obs("PauliY", [0]), named_obs("PauliX", [2])]), + ], + ) hamiltonian_obs( np.array([1], dtype=r_dtype), [tensor_prod_obs([named_obs("PauliX", [2]), named_obs("PauliY", [0])])], From db1c6f648be0a268d9bb7b8b02791ec92de03455 Mon Sep 17 00:00:00 2001 From: Dev version update bot Date: Fri, 22 Mar 2024 19:05:13 +0000 Subject: [PATCH 280/428] Auto update version --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index 4089152989..ec9ead0f50 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.36.0-dev17" +__version__ = "0.36.0-dev18" From ee0f93db5fef17c04834bcf9d723c58fc20c5cdf Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Fri, 22 Mar 2024 15:08:19 -0400 Subject: [PATCH 281/428] windows.yaml tests --- .github/workflows/tests_windows.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests_windows.yml b/.github/workflows/tests_windows.yml index c609d26dea..8d01b2fdcb 100644 --- a/.github/workflows/tests_windows.yml +++ b/.github/workflows/tests_windows.yml @@ -117,8 +117,8 @@ jobs: - name: Get site-packages directory run: | - pythonSitePackages = $(python -c "import site; print(site.getsitepackages()[0])") - echo $pythonSitePackages + $pythonSitePackages = & python -c "import site; print(site.getsitepackages()[0])" + Write-Host $pythonSitePackages echo "PYTHON_SITE_PACKAGES=$pythonSitePackages" >> $GITHUB_ENV - name: Build and run unit tests for code coverage From 3992d20067184951c70e91b95ee64a70cd8a901c Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Fri, 22 Mar 2024 15:13:54 -0400 Subject: [PATCH 282/428] check child directory of site-packages --- .github/workflows/tests_windows.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests_windows.yml b/.github/workflows/tests_windows.yml index 8d01b2fdcb..dcf81c7827 100644 --- a/.github/workflows/tests_windows.yml +++ b/.github/workflows/tests_windows.yml @@ -118,7 +118,9 @@ jobs: - name: Get site-packages directory run: | $pythonSitePackages = & python -c "import site; print(site.getsitepackages()[0])" - Write-Host $pythonSitePackages + Get-ChildItem $pythonSitePackages + $pythonSitePackage += "\Lib\site-packages\scip.libs\" + Get-ChildItem $pythonSitePackages echo "PYTHON_SITE_PACKAGES=$pythonSitePackages" >> $GITHUB_ENV - name: Build and run unit tests for code coverage From cdb00fd0a021c4ead4bcf0cfecddeed85fc06ab9 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Fri, 22 Mar 2024 15:19:36 -0400 Subject: [PATCH 283/428] quick fix --- .github/workflows/tests_windows.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests_windows.yml b/.github/workflows/tests_windows.yml index dcf81c7827..09e2965550 100644 --- a/.github/workflows/tests_windows.yml +++ b/.github/workflows/tests_windows.yml @@ -117,10 +117,10 @@ jobs: - name: Get site-packages directory run: | - $pythonSitePackages = & python -c "import site; print(site.getsitepackages()[0])" - Get-ChildItem $pythonSitePackages - $pythonSitePackage += "\Lib\site-packages\scip.libs\" + $pythonSitePackages = & python -c "import site; print( f'{site.getsitepackages()[0]}/Lib')")" Get-ChildItem $pythonSitePackages + $pythonSitePackages2 = & python -c "import site; print( f'{site.getsitepackages()[0]}/libs')")" + Get-ChildItem $pythonSitePackages2 echo "PYTHON_SITE_PACKAGES=$pythonSitePackages" >> $GITHUB_ENV - name: Build and run unit tests for code coverage From a17216bdaa05fa4f48772c9ceb98c6430bf80b80 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Fri, 22 Mar 2024 15:23:11 -0400 Subject: [PATCH 284/428] fix typo --- .github/workflows/tests_windows.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests_windows.yml b/.github/workflows/tests_windows.yml index 09e2965550..67e33e81ae 100644 --- a/.github/workflows/tests_windows.yml +++ b/.github/workflows/tests_windows.yml @@ -117,9 +117,9 @@ jobs: - name: Get site-packages directory run: | - $pythonSitePackages = & python -c "import site; print( f'{site.getsitepackages()[0]}/Lib')")" + $pythonSitePackages = & python -c "import site; print( f'{site.getsitepackages()[0]}/Lib')" Get-ChildItem $pythonSitePackages - $pythonSitePackages2 = & python -c "import site; print( f'{site.getsitepackages()[0]}/libs')")" + $pythonSitePackages2 = & python -c "import site; print( f'{site.getsitepackages()[0]}/libs')" Get-ChildItem $pythonSitePackages2 echo "PYTHON_SITE_PACKAGES=$pythonSitePackages" >> $GITHUB_ENV From 9c9eb1e3fe247783ccec49102b55905d72c77e5c Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Fri, 22 Mar 2024 15:30:01 -0400 Subject: [PATCH 285/428] set python site-packages with cmake --- CMakeLists.txt | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 523a65da2f..fa5b5b16ab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -81,8 +81,17 @@ set(CMAKE_POLICY_DEFAULT_CMP0127 NEW) # To suppress pybind11 CMP0127 warning # Add pybind11 include(FetchContent) +find_package(Python COMPONENTS Interpreter Development) + +execute_process( + COMMAND ${PYTHON_EXECUTABLE} -c "import site; print(site.getsitepackages()[0])" + OUTPUT_VARIABLE PYTHON_SITE_PACKAGES + OUTPUT_STRIP_TRAILING_WHITESPACE +) + +message("Python site-packages directory: ${PYTHON_SITE_PACKAGES}") + if(ENABLE_PYTHON) - find_package(Python COMPONENTS Interpreter Development) FetchContent_Declare(pybind11 GIT_REPOSITORY https://github.com/pybind/pybind11.git GIT_TAG v2.11.1 From 2cd983f722de7bb6f396c04dd14dc85f371579dc Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Fri, 22 Mar 2024 15:32:10 -0400 Subject: [PATCH 286/428] remove package_site env set from windows.yml --- .github/workflows/tests_windows.yml | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/.github/workflows/tests_windows.yml b/.github/workflows/tests_windows.yml index 67e33e81ae..7380442b19 100644 --- a/.github/workflows/tests_windows.yml +++ b/.github/workflows/tests_windows.yml @@ -114,14 +114,6 @@ jobs: run: | choco install OpenCppCoverage -y echo "C:\Program Files\OpenCppCoverage" >> $env:GITHUB_PATH - - - name: Get site-packages directory - run: | - $pythonSitePackages = & python -c "import site; print( f'{site.getsitepackages()[0]}/Lib')" - Get-ChildItem $pythonSitePackages - $pythonSitePackages2 = & python -c "import site; print( f'{site.getsitepackages()[0]}/libs')" - Get-ChildItem $pythonSitePackages2 - echo "PYTHON_SITE_PACKAGES=$pythonSitePackages" >> $GITHUB_ENV - name: Build and run unit tests for code coverage run: | @@ -204,13 +196,6 @@ jobs: run: | choco install OpenCppCoverage -y echo "C:\Program Files\OpenCppCoverage" >> $env:GITHUB_PATH - - - name: Get site-packages directory - id: get_site_packages - shell: pwsh - run: | - $pythonSitePackages = $(python -c "import site; print(site.getsitepackages()[0])") - echo "PYTHON_SITE_PACKAGES=$pythonSitePackages" >> $GITHUB_ENV - name: Build and run unit tests for code coverage run: | From 70289a6b39ac8090a1b531971ba1a4468ff8e217 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Fri, 22 Mar 2024 15:44:19 -0400 Subject: [PATCH 287/428] fix cmake package_sites get --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fa5b5b16ab..67d9050546 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -84,7 +84,7 @@ include(FetchContent) find_package(Python COMPONENTS Interpreter Development) execute_process( - COMMAND ${PYTHON_EXECUTABLE} -c "import site; print(site.getsitepackages()[0])" + COMMAND "${PYTHON_EXECUTABLE}" "-c" "import site; print(site.getsitepackages()[0])" OUTPUT_VARIABLE PYTHON_SITE_PACKAGES OUTPUT_STRIP_TRAILING_WHITESPACE ) From f113939c123719daa0c6086a63dda88c258e144c Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Fri, 22 Mar 2024 15:51:31 -0400 Subject: [PATCH 288/428] check Python_SITELIB variable --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 67d9050546..6fb140b924 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -89,7 +89,7 @@ execute_process( OUTPUT_STRIP_TRAILING_WHITESPACE ) -message("Python site-packages directory: ${PYTHON_SITE_PACKAGES}") +message("Python site-packages directory: ${Python_SITELIB}") if(ENABLE_PYTHON) FetchContent_Declare(pybind11 From 0aa79355f9ec32116b73192e4a2b5211f008b360 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Fri, 22 Mar 2024 16:26:45 -0400 Subject: [PATCH 289/428] check if env is correctly set --- CMakeLists.txt | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6fb140b924..fe72ab631f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -83,14 +83,17 @@ include(FetchContent) find_package(Python COMPONENTS Interpreter Development) -execute_process( - COMMAND "${PYTHON_EXECUTABLE}" "-c" "import site; print(site.getsitepackages()[0])" - OUTPUT_VARIABLE PYTHON_SITE_PACKAGES - OUTPUT_STRIP_TRAILING_WHITESPACE -) - message("Python site-packages directory: ${Python_SITELIB}") +if(CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME MATCHES "Windows") + set(SCIPY_PATH "${Python_SITELIB}/scipy.libs") + if(NOT EXISTS SCIPY_PATH) + message(FATAL_ERROR "Can't find the scipy.libs in the python package_sites.") + endif() + set(ENV{SCIPY_LIBS} ${SCIPY_PATH}) + message("$ENV{SCIPY_LIBS}") +endif() + if(ENABLE_PYTHON) FetchContent_Declare(pybind11 GIT_REPOSITORY https://github.com/pybind/pybind11.git From f79d96f1de168a1a0e25d156918be1fe313e857e Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Fri, 22 Mar 2024 16:32:15 -0400 Subject: [PATCH 290/428] install scipy with linux cpp test --- .github/workflows/tests_linux.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/tests_linux.yml b/.github/workflows/tests_linux.yml index ecf5fdb6be..ef2d8e6788 100644 --- a/.github/workflows/tests_linux.yml +++ b/.github/workflows/tests_linux.yml @@ -42,6 +42,9 @@ jobs: name: Install Python with: python-version: '3.9' + + - name: Get required Python packages + run: python -m pip install scipy - name: Checkout PennyLane-Lightning uses: actions/checkout@v3 @@ -215,6 +218,9 @@ jobs: - name: Checkout PennyLane-Lightning uses: actions/checkout@v3 + + - name: Get required Python packages + run: python -m pip install scipy - name: Install dependencies run: sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION liblapack-dev libopenblas-dev ninja-build gcovr lcov From ea8f3c861e8dac96ec258232ca983de0b281132d Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Fri, 22 Mar 2024 19:27:46 -0400 Subject: [PATCH 291/428] use config file to pass path scipy to cpp layer --- CMakeLists.txt | 15 ++-- .../core/src/utils/UtilLinearAlg.hpp | 70 +++++++++---------- .../core/src/utils/config.h.in | 4 ++ 3 files changed, 47 insertions(+), 42 deletions(-) create mode 100644 pennylane_lightning/core/src/utils/config.h.in diff --git a/CMakeLists.txt b/CMakeLists.txt index fe72ab631f..b273c840fb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -86,14 +86,15 @@ find_package(Python COMPONENTS Interpreter Development) message("Python site-packages directory: ${Python_SITELIB}") if(CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME MATCHES "Windows") - set(SCIPY_PATH "${Python_SITELIB}/scipy.libs") - if(NOT EXISTS SCIPY_PATH) - message(FATAL_ERROR "Can't find the scipy.libs in the python package_sites.") + set(SCIPY_PATH ${Python_SITELIB}) + string(APPEND SCIPY_PATH "/scipy.libs") + message(STATUS "${SCIPY_PATH}") + if(NOT EXISTS ${SCIPY_PATH}) + message(FATAL_ERROR "Can't find the scipy.libs in the python package_sites.") endif() - set(ENV{SCIPY_LIBS} ${SCIPY_PATH}) - message("$ENV{SCIPY_LIBS}") -endif() - + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h) + endif() + if(ENABLE_PYTHON) FetchContent_Declare(pybind11 GIT_REPOSITORY https://github.com/pybind/pybind11.git diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index 535bafcb6d..c5c4838f18 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -34,6 +34,10 @@ #include #endif +#ifndef __APPLE__ +#include "config.h" +#endif + #include "SharedLibLoader.hpp" /// @cond DEV @@ -107,49 +111,45 @@ void compute_diagonalizing_gates(int n, int lda, std::shared_ptr blasLib = std::make_shared(libName); #else -#if defined(__linux__) - const std::string defaultLibName = "liblapack.so"; -#else - const std::string defaultLibName = "libopenblas.dll"; -#endif - std::shared_ptr blasLib = - std::make_shared(defaultLibName); + std::shared_ptr blasLib; std::vector> blasLibs; - if (!blasLib->getHandle()) { - const std::string currentPathStr(getPath()); - const std::filesystem::path currentPath(currentPathStr); - const auto scipyLibsPath = - currentPath.parent_path().parent_path() / "scipy.libs"; - - std::vector> availableLibs; - - for (const auto &lib : - std::filesystem::directory_iterator(scipyLibsPath)) { - if (lib.is_regular_file()) { - for (const auto &iter : priority_lib) { - std::string libname_str = lib.path().filename().string(); - if (libname_str.find(iter.first) != std::string ::npos) { - availableLibs.emplace_back( - std::make_pair(libname_str, iter.second)); - } + + const std::string currentPathStr(SCIPY_LIBS_PATH); + std::filesystem::path scipyLibsPath; + + if (std::filesystem::exists(currentPathStr)) { + scipyLibsPath = currentPathStr; + } else { + std::filesystem::path currentPath(getPath()); + scipyLibsPath = currentPath.parent_path().parent_path() / "scipy.libs"; + } + + std::vector> availableLibs; + + for (const auto &lib : std::filesystem::directory_iterator(scipyLibsPath)) { + if (lib.is_regular_file()) { + for (const auto &iter : priority_lib) { + std::string libname_str = lib.path().filename().string(); + if (libname_str.find(iter.first) != std::string ::npos) { + availableLibs.emplace_back( + std::make_pair(libname_str, iter.second)); } } } + } - std::sort(availableLibs.begin(), availableLibs.end(), - [](const auto &lhs, const auto &rhs) { - return lhs.second < rhs.second; - }); + std::sort(availableLibs.begin(), availableLibs.end(), + [](const auto &lhs, const auto &rhs) { + return lhs.second < rhs.second; + }); - for (const auto &lib : availableLibs) { - auto libPath = scipyLibsPath / lib.first.c_str(); - const std::string libPathStr = libPath.string().c_str(); - blasLibs.emplace_back( - std::make_shared(libPathStr)); - } - blasLib = blasLibs.back(); + for (const auto &lib : availableLibs) { + auto libPath = scipyLibsPath / lib.first.c_str(); + const std::string libPathStr = libPath.string().c_str(); + blasLibs.emplace_back(std::make_shared(libPathStr)); } + blasLib = blasLibs.back(); #endif char jobz = 'V'; // Enable both eigenvalues and eigenvectors computation diff --git a/pennylane_lightning/core/src/utils/config.h.in b/pennylane_lightning/core/src/utils/config.h.in new file mode 100644 index 0000000000..0c4249e705 --- /dev/null +++ b/pennylane_lightning/core/src/utils/config.h.in @@ -0,0 +1,4 @@ +#ifndef CONFIG_H +#define CONFIG_H +#define SCIPY_LIBS_PATH "${SCIPY_PATH}" +#endif \ No newline at end of file From 5e3552d5f65e14d8a26fe2527afd0f6f1e196e9b Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Fri, 22 Mar 2024 19:42:13 -0400 Subject: [PATCH 292/428] remove liblapack_dev install in ci work flows --- .github/workflows/tests_gpu_cuda.yml | 3 +- .github/workflows/tests_gpu_kokkos.yml | 7 +- .github/workflows/tests_linux.yml | 75 ++++--------------- .github/workflows/tests_linux_x86_mpi_gpu.yml | 2 - 4 files changed, 21 insertions(+), 66 deletions(-) diff --git a/.github/workflows/tests_gpu_cuda.yml b/.github/workflows/tests_gpu_cuda.yml index d45c34fe4d..f35dfa24a6 100644 --- a/.github/workflows/tests_gpu_cuda.yml +++ b/.github/workflows/tests_gpu_cuda.yml @@ -119,8 +119,7 @@ jobs: - name: Install required packages run: | - python -m pip install ninja cmake custatevec-cu${{ matrix.cuda_version }} - sudo apt-get -y -q install liblapack-dev + python -m pip install ninja cmake scipy custatevec-cu${{ matrix.cuda_version }} - name: Build and run unit tests run: | diff --git a/.github/workflows/tests_gpu_kokkos.yml b/.github/workflows/tests_gpu_kokkos.yml index 89fac0aa43..674557093d 100644 --- a/.github/workflows/tests_gpu_kokkos.yml +++ b/.github/workflows/tests_gpu_kokkos.yml @@ -147,7 +147,10 @@ jobs: python-version: '3.9' - name: Install dependencies - run: sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION ninja-build gcovr lcov liblapack-dev + run: | + sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION ninja-build gcovr lcov + python -m pip install scipy + - name: Build and run unit tests run: | @@ -251,7 +254,7 @@ jobs: python-version: '3.9' - name: Install dependencies - run: sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION liblapack-dev + run: sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION - name: Get required Python packages run: | diff --git a/.github/workflows/tests_linux.yml b/.github/workflows/tests_linux.yml index ef2d8e6788..68a17838f7 100644 --- a/.github/workflows/tests_linux.yml +++ b/.github/workflows/tests_linux.yml @@ -50,10 +50,11 @@ jobs: uses: actions/checkout@v3 - name: Install dependencies - run: echo ${{ github.event_name }} && sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION liblapack-dev ninja-build gcovr lcov + run: echo ${{ github.event_name }} && sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION ninja-build gcovr lcov - name: Build and run unit tests run: | + set -x cmake . -BBuild -G Ninja \ -DCMAKE_BUILD_TYPE=Debug \ -DBUILD_TESTS=ON \ @@ -223,10 +224,11 @@ jobs: run: python -m pip install scipy - name: Install dependencies - run: sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION liblapack-dev libopenblas-dev ninja-build gcovr lcov + run: sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION libopenblas-dev ninja-build gcovr lcov - name: Build and run unit tests run: | + set -x cmake . -BBuild -G Ninja \ -DCMAKE_BUILD_TYPE=Debug \ -DENABLE_PYTHON=OFF \ @@ -241,61 +243,6 @@ jobs: for file in *runner ; do ./$file --order lex --reporter junit --out ./tests/results/report_$file.xml; done; lcov --directory . -b ../pennylane_lightning/core/src --capture --output-file coverage.info lcov --remove coverage.info '/usr/*' --output-file coverage.info - mv coverage.info coverage-${{ github.job }}-${{ matrix.pl_backend }}-Lapack.info - - - name: Upload test results - uses: actions/upload-artifact@v3 - if: always() - with: - name: ubuntu-tests-reports-${{ github.job }}-${{ matrix.pl_backend }}-Lapack - path: ./Build/tests/results/ - if-no-files-found: error - - - name: Upload code coverage results - uses: actions/upload-artifact@v3 - with: - name: ubuntu-codecov-results-cpp - path: ./Build/coverage-${{ github.job }}-${{ matrix.pl_backend }}-Lapack.info - if-no-files-found: error - - cpptestswithOpenBLASNoLAPACK: - if: ${{ !contains(fromJSON('["schedule", "workflow_dispatch"]'), github.event_name) }} - strategy: - matrix: - os: [ubuntu-22.04] - pl_backend: ["lightning_qubit"] - timeout-minutes: 30 - name: C++ tests (OpenBLAS without LAPACK) - runs-on: ${{ matrix.os }} - - steps: - - uses: actions/setup-python@v4 - name: Install Python - with: - python-version: '3.9' - - - name: Checkout PennyLane-Lightning - uses: actions/checkout@v3 - - - name: Install dependencies - run: sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION liblapack-dev libopenblas-dev ninja-build gcovr lcov - - - name: Build and run unit tests - run: | - cmake . -BBuild -G Ninja \ - -DCMAKE_BUILD_TYPE=Debug \ - -DENABLE_PYTHON=OFF \ - -DPL_BACKEND=${{ matrix.pl_backend }} \ - -DBUILD_TESTS=ON \ - -DCMAKE_CXX_COMPILER=$(which g++-$GCC_VERSION) \ - -DENABLE_COVERAGE=ON \ - -DLQ_ENABLE_KERNEL_OMP=ON - cmake --build ./Build - cd ./Build - mkdir -p ./tests/results - for file in *runner ; do ./$file --order lex --reporter junit --out ./tests/results/report_$file.xml; done; - lcov --directory . -b ../pennylane_lightning/core/src --capture --output-file coverage.info - lcov --remove coverage.info '/usr/*' --output-file coverage.info mv coverage.info coverage-${{ github.job }}-${{ matrix.pl_backend }}.info - name: Upload test results @@ -313,6 +260,7 @@ jobs: path: ./Build/coverage-${{ github.job }}-${{ matrix.pl_backend }}.info if-no-files-found: error + pythontestswithOpenBLAS: strategy: matrix: @@ -349,6 +297,9 @@ jobs: name: Install Python with: python-version: '3.9' + + - name: Get required Python packages + run: python -m pip install scipy - name: Install dependencies run: sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION libopenblas-dev @@ -449,12 +400,16 @@ jobs: rm -rf Kokkos mkdir Kokkos/ cp -rf ${{ github.workspace}}/Kokkos_install/${{ matrix.exec_model }}/* Kokkos/ + + - name: Get required Python packages + run: python -m pip install scipy - name: Install dependencies - run: sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION liblapack-dev ninja-build gcovr lcov + run: sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION ninja-build gcovr lcov - name: Build and run unit tests run: | + set -x cmake . -BBuild -G Ninja \ -DCMAKE_BUILD_TYPE=Debug \ -DBUILD_TESTS=ON \ @@ -658,7 +613,7 @@ jobs: token: ${{ secrets.CODECOV_TOKEN }} upload-to-codecov-linux-cpp: - needs: [cpptests, cpptestswithOpenBLAS, cpptestswithOpenBLASNoLAPACK, cpptestswithKokkos] + needs: [cpptests, cpptestswithOpenBLAS, cpptestswithKokkos] name: Upload cpp coverage data to codecov runs-on: ubuntu-latest steps: @@ -713,7 +668,7 @@ jobs: cp -rf ${{ github.workspace}}/Kokkos_install/${{ matrix.exec_model }}/* Kokkos/ - name: Install dependencies - run: sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION liblapack-dev ninja-build gcovr lcov + run: sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION ninja-build gcovr lcov - name: Build and run unit tests run: | diff --git a/.github/workflows/tests_linux_x86_mpi_gpu.yml b/.github/workflows/tests_linux_x86_mpi_gpu.yml index da4fe1b077..7a67ae0147 100644 --- a/.github/workflows/tests_linux_x86_mpi_gpu.yml +++ b/.github/workflows/tests_linux_x86_mpi_gpu.yml @@ -95,7 +95,6 @@ jobs: run: | python -m pip install -r requirements-dev.txt python -m pip install cmake custatevec-cu12 - sudo apt-get -y -q install liblapack-dev - name: Validate GPU version and installed compiler and modules run: | @@ -118,7 +117,6 @@ jobs: run: | source /etc/profile.d/modules.sh && module use /opt/modules/ && module load ${{ matrix.mpilib }}/cuda-${{ matrix.cuda_version_maj }}.${{ matrix.cuda_version_min }} export CUQUANTUM_SDK=$(python -c "import site; print( f'{site.getsitepackages()[0]}/cuquantum/lib')") - export LD_LIBRARY_PATH=$(python -c "import site; print( f'{site.getsitepackages()[0]}/scipy.libs/')"):$LD_LIBRARY_PATH cmake . -BBuild \ -DPL_BACKEND=lightning_gpu \ -DENABLE_MPI=ON \ From b9e65cadf2c6dc90e9b141df245dfd506d2c663c Mon Sep 17 00:00:00 2001 From: Dev version update bot Date: Fri, 22 Mar 2024 23:45:28 +0000 Subject: [PATCH 293/428] Auto update version --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index 4089152989..ec9ead0f50 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.36.0-dev17" +__version__ = "0.36.0-dev18" From a3d9a06ac7d1219e9af920f2c51e0e0ab56fbdde Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Fri, 22 Mar 2024 19:47:39 -0400 Subject: [PATCH 294/428] Trigger CI From 26a338d88a206f7eac6f10837ab5d11c43e0e3a4 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Fri, 22 Mar 2024 19:51:14 -0400 Subject: [PATCH 295/428] fix typo of path to scipy in windows with cmake --- CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b273c840fb..f112ab32cc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -87,7 +87,11 @@ message("Python site-packages directory: ${Python_SITELIB}") if(CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME MATCHES "Windows") set(SCIPY_PATH ${Python_SITELIB}) - string(APPEND SCIPY_PATH "/scipy.libs") + if(CMAKE_SYSTEM_NAME MATCHES "Linux") + string(APPEND SCIPY_PATH "/scipy.libs") + else() + string(APPEND SCIPY_PATH "\\scipy.libs") + endif() message(STATUS "${SCIPY_PATH}") if(NOT EXISTS ${SCIPY_PATH}) message(FATAL_ERROR "Can't find the scipy.libs in the python package_sites.") From 663f503bc9c8c7462782a7ede8f5d51cfb5fa2da Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Fri, 22 Mar 2024 19:56:16 -0400 Subject: [PATCH 296/428] fix for linux ci --- .github/workflows/tests_linux.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests_linux.yml b/.github/workflows/tests_linux.yml index 35ac9ee901..ca7dc00118 100644 --- a/.github/workflows/tests_linux.yml +++ b/.github/workflows/tests_linux.yml @@ -670,7 +670,9 @@ jobs: cp -rf ${{ github.workspace}}/Kokkos_install/${{ matrix.exec_model }}/* Kokkos/ - name: Install dependencies - run: sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION ninja-build gcovr lcov + run: | + sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION ninja-build gcovr lcov + python -m pip install scipy - name: Build and run unit tests run: | From 7e0e5c8cac3b253a68a174c53419c22a101d1fc6 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Fri, 22 Mar 2024 19:58:51 -0400 Subject: [PATCH 297/428] install python and scipy for window cpp tests --- .github/workflows/tests_windows.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/tests_windows.yml b/.github/workflows/tests_windows.yml index 7380442b19..66bcce17ed 100644 --- a/.github/workflows/tests_windows.yml +++ b/.github/workflows/tests_windows.yml @@ -103,6 +103,11 @@ jobs: os: [windows-latest] pl_backend: ["lightning_qubit"] steps: + - uses: actions/setup-python@v4 + name: Install Python + with: + python-version: '3.9' + - name: Checkout PennyLane-Lightning uses: actions/checkout@v3 @@ -168,6 +173,11 @@ jobs: runs-on: ${{ matrix.os }} steps: + - uses: actions/setup-python@v4 + name: Install Python + with: + python-version: '3.9' + - name: Restoring cached Kokkos id: kokkos-cache uses: actions/cache@v4 From 771f666797d56eb64fa0c4a77c6134491e10d063 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Fri, 22 Mar 2024 20:14:26 -0400 Subject: [PATCH 298/428] debug windows path --- pennylane_lightning/core/src/utils/UtilLinearAlg.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index c5c4838f18..c08a19820a 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -27,6 +27,8 @@ #include #include +#include + #if defined(__APPLE__) || defined(__linux__) #include #elif defined(_MSC_VER) @@ -120,6 +122,7 @@ void compute_diagonalizing_gates(int n, int lda, if (std::filesystem::exists(currentPathStr)) { scipyLibsPath = currentPathStr; + std::cout< Date: Fri, 22 Mar 2024 20:38:50 -0400 Subject: [PATCH 299/428] test for windows --- CMakeLists.txt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f112ab32cc..1f614f440b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -85,8 +85,13 @@ find_package(Python COMPONENTS Interpreter Development) message("Python site-packages directory: ${Python_SITELIB}") +execute_process( + COMMAND ${Python_EXECUTABLE} -c "import site; print(site.getsitepackages()[0])" + OUTPUT_VARIABLE Python_PACKAGES_SITE + OUTPUT_STRIP_TRAILING_WHITESPACE) + if(CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME MATCHES "Windows") - set(SCIPY_PATH ${Python_SITELIB}) + set(SCIPY_PATH ${Python_PACKAGES_SITE}) if(CMAKE_SYSTEM_NAME MATCHES "Linux") string(APPEND SCIPY_PATH "/scipy.libs") else() @@ -98,7 +103,7 @@ if(CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME MATCHES "Windows") endif() configure_file(${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h) endif() - + if(ENABLE_PYTHON) FetchContent_Declare(pybind11 GIT_REPOSITORY https://github.com/pybind/pybind11.git From b71cf514419df20750de1e892757104524bdc31f Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Fri, 22 Mar 2024 20:39:41 -0400 Subject: [PATCH 300/428] remove iostream in UtilLinearAlg.hpp --- pennylane_lightning/core/src/utils/UtilLinearAlg.hpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index c08a19820a..c5c4838f18 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -27,8 +27,6 @@ #include #include -#include - #if defined(__APPLE__) || defined(__linux__) #include #elif defined(_MSC_VER) @@ -122,7 +120,6 @@ void compute_diagonalizing_gates(int n, int lda, if (std::filesystem::exists(currentPathStr)) { scipyLibsPath = currentPathStr; - std::cout< Date: Fri, 22 Mar 2024 20:48:10 -0400 Subject: [PATCH 301/428] add specific scipy lib path for windows --- CMakeLists.txt | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1f614f440b..651414871c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -85,17 +85,16 @@ find_package(Python COMPONENTS Interpreter Development) message("Python site-packages directory: ${Python_SITELIB}") -execute_process( - COMMAND ${Python_EXECUTABLE} -c "import site; print(site.getsitepackages()[0])" - OUTPUT_VARIABLE Python_PACKAGES_SITE - OUTPUT_STRIP_TRAILING_WHITESPACE) if(CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME MATCHES "Windows") - set(SCIPY_PATH ${Python_PACKAGES_SITE}) if(CMAKE_SYSTEM_NAME MATCHES "Linux") - string(APPEND SCIPY_PATH "/scipy.libs") + set(SCIPY_PATH ${Python_SITELIB}) else() - string(APPEND SCIPY_PATH "\\scipy.libs") + execute_process( + COMMAND ${Python_EXECUTABLE} -c "import site; print( f'{site.getsitepackages()[0]}/Lib/site-packages/scipy.libs')" + OUTPUT_VARIABLE Python_PACKAGES_SITE + OUTPUT_STRIP_TRAILING_WHITESPACE) + set(SCIPY_PATH ${Python_PACKAGES_SITE}) endif() message(STATUS "${SCIPY_PATH}") if(NOT EXISTS ${SCIPY_PATH}) From d872a1dbc5ad71ea4642979d269348d2c1ca1409 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Fri, 22 Mar 2024 21:37:40 -0400 Subject: [PATCH 302/428] replace \ with / in windows --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 651414871c..83a73376a5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -91,7 +91,7 @@ if(CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME MATCHES "Windows") set(SCIPY_PATH ${Python_SITELIB}) else() execute_process( - COMMAND ${Python_EXECUTABLE} -c "import site; print( f'{site.getsitepackages()[0]}/Lib/site-packages/scipy.libs')" + COMMAND ${Python_EXECUTABLE} -c "import site; print(str(r'{}'.format(site.getsitepackages()[0]).replace(r'\\', '/')))" OUTPUT_VARIABLE Python_PACKAGES_SITE OUTPUT_STRIP_TRAILING_WHITESPACE) set(SCIPY_PATH ${Python_PACKAGES_SITE}) From dbbb8ceac7387f092b9341a328bf949fd272c48a Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Fri, 22 Mar 2024 22:09:35 -0400 Subject: [PATCH 303/428] use cmake string replace to replace backslash --- CMakeLists.txt | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 83a73376a5..72d1ae942a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -86,15 +86,19 @@ find_package(Python COMPONENTS Interpreter Development) message("Python site-packages directory: ${Python_SITELIB}") +#set(my_string "\\a\\b") +#message("+++++++++++++++++${my_string}") +#string(REPLACE "\\" "/" my_string ${my_string}) +#message("+++++++++++++++++${my_string}") + + if(CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME MATCHES "Windows") + set(SCIPY_PATH ${Python_SITELIB}) if(CMAKE_SYSTEM_NAME MATCHES "Linux") - set(SCIPY_PATH ${Python_SITELIB}) + string(APPEND SCIPY_PATH "/scipy.libs") else() - execute_process( - COMMAND ${Python_EXECUTABLE} -c "import site; print(str(r'{}'.format(site.getsitepackages()[0]).replace(r'\\', '/')))" - OUTPUT_VARIABLE Python_PACKAGES_SITE - OUTPUT_STRIP_TRAILING_WHITESPACE) - set(SCIPY_PATH ${Python_PACKAGES_SITE}) + string(REPLACE "\\" "/" SCIPY_PATH ${SCIPY_PATH}) + string(APPEND SCIPY_PATH "/scipy.libs") endif() message(STATUS "${SCIPY_PATH}") if(NOT EXISTS ${SCIPY_PATH}) From 47082d420975b98114b44e66cebae39df692ab8d Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Fri, 22 Mar 2024 22:28:12 -0400 Subject: [PATCH 304/428] tidy up code --- CMakeLists.txt | 6 ------ .../core/src/observables/Observables.hpp | 2 -- .../core/src/utils/SharedLibLoader.hpp | 6 +++--- .../core/src/utils/UtilLinearAlg.hpp | 17 ++++++++--------- 4 files changed, 11 insertions(+), 20 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 72d1ae942a..184e2d2938 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -86,12 +86,6 @@ find_package(Python COMPONENTS Interpreter Development) message("Python site-packages directory: ${Python_SITELIB}") -#set(my_string "\\a\\b") -#message("+++++++++++++++++${my_string}") -#string(REPLACE "\\" "/" my_string ${my_string}) -#message("+++++++++++++++++${my_string}") - - if(CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME MATCHES "Windows") set(SCIPY_PATH ${Python_SITELIB}) if(CMAKE_SYSTEM_NAME MATCHES "Linux") diff --git a/pennylane_lightning/core/src/observables/Observables.hpp b/pennylane_lightning/core/src/observables/Observables.hpp index 69b57c187d..5e8ba3831f 100644 --- a/pennylane_lightning/core/src/observables/Observables.hpp +++ b/pennylane_lightning/core/src/observables/Observables.hpp @@ -217,8 +217,6 @@ class HermitianObsBase : public Observable { protected: MatrixT matrix_; std::vector wires_; - - private: std::vector eigenVals_; MatrixT unitary_; diff --git a/pennylane_lightning/core/src/utils/SharedLibLoader.hpp b/pennylane_lightning/core/src/utils/SharedLibLoader.hpp index 26bbab644e..551351448f 100644 --- a/pennylane_lightning/core/src/utils/SharedLibLoader.hpp +++ b/pennylane_lightning/core/src/utils/SharedLibLoader.hpp @@ -69,12 +69,12 @@ class SharedLibLoader final { void *getHandle() { return handle_; } - auto getSymbol(const std::string &symbol) { + void *getSymbol(const std::string &symbol) { #if defined(__APPLE__) || defined(__linux__) - auto sym = dlsym(handle_, symbol.c_str()); + void *sym = dlsym(handle_, symbol.c_str()); PL_ABORT_IF(!sym, dlerror()); #elif defined(_MSC_VER) - auto sym = GetProcAddress(handle_, symbol.c_str()); + void *sym = GetProcAddress(handle_, symbol.c_str()); PL_ABORT_IF(!handle_, std::to_string(GetLastError())); #endif return sym; diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index c5c4838f18..e08812ddc3 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -43,12 +43,12 @@ /// @cond DEV namespace { // LAPACK routine for complex Hermitian eigensystems -typedef void (*zheevPtr)(const char *, const char *, const int *, - std::complex *, const int *, double *, - std::complex *, const int *, double *, int *); -typedef void (*cheevPtr)(const char *, const char *, const int *, - std::complex *, const int *, float *, - std::complex *, const int *, float *, int *); +using zheevPtr = void (*)(const char *, const char *, const int *, + std::complex *, const int *, double *, + std::complex *, const int *, double *, int *); +using cheevPtr = void (*)(const char *, const char *, const int *, + std::complex *, const int *, float *, + std::complex *, const int *, float *, int *); std::unordered_map priority_lib = { {"stdc", 0}, {"gcc", 1}, {"quadmath", 2}, {"gfortran", 3}, {"openblas", 4}}; @@ -132,8 +132,7 @@ void compute_diagonalizing_gates(int n, int lda, for (const auto &iter : priority_lib) { std::string libname_str = lib.path().filename().string(); if (libname_str.find(iter.first) != std::string ::npos) { - availableLibs.emplace_back( - std::make_pair(libname_str, iter.second)); + availableLibs.emplace_back(libname_str, iter.second); } } } @@ -146,7 +145,7 @@ void compute_diagonalizing_gates(int n, int lda, for (const auto &lib : availableLibs) { auto libPath = scipyLibsPath / lib.first.c_str(); - const std::string libPathStr = libPath.string().c_str(); + const std::string libPathStr = libPath.string(); blasLibs.emplace_back(std::make_shared(libPathStr)); } blasLib = blasLibs.back(); From 8d426447655949f638fe29590cd952d844de35ff Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Fri, 22 Mar 2024 22:52:09 -0400 Subject: [PATCH 305/428] codeFactor [skip] & add more win32 cpp tests --- .../src/measurements/tests/Test_MeasurementsBase.cpp | 12 ------------ .../core/src/observables/tests/Test_Observables.cpp | 2 -- pennylane_lightning/core/src/utils/UtilLinearAlg.hpp | 4 ++-- 3 files changed, 2 insertions(+), 16 deletions(-) diff --git a/pennylane_lightning/core/src/measurements/tests/Test_MeasurementsBase.cpp b/pennylane_lightning/core/src/measurements/tests/Test_MeasurementsBase.cpp index a0fb9ed3b2..1b723f7212 100644 --- a/pennylane_lightning/core/src/measurements/tests/Test_MeasurementsBase.cpp +++ b/pennylane_lightning/core/src/measurements/tests/Test_MeasurementsBase.cpp @@ -570,7 +570,6 @@ TEST_CASE("Expval Shot- NamedObs", "[MeasurementsBase][Observables]") { } } -#ifndef _MSC_VER template void testHermitianObsExpvalShot() { if constexpr (!std::is_same_v) { using StateVectorT = typename TypeList::Type; @@ -673,7 +672,6 @@ TEST_CASE("Expval Shot - HermitianObs ", "[MeasurementsBase][Observables]") { testHermitianObsExpvalShot(); } } -#endif template void testHermitianObsExpval() { if constexpr (!std::is_same_v) { @@ -839,7 +837,6 @@ template void testTensorProdObsExpvalShot() { expected, static_cast(0.20))); } -#ifndef _MSC_VER DYNAMIC_SECTION(" With Identity and shots_range" << StateVectorToName::name) { size_t num_shots = 80000; @@ -860,7 +857,6 @@ template void testTensorProdObsExpvalShot() { REQUIRE_THAT(result, Catch::Matchers::WithinRel( expected, static_cast(0.20))); } -#endif testTensorProdObsExpvalShot(); } @@ -1011,7 +1007,6 @@ TEST_CASE("Var - HermitianObs", "[MeasurementsBase][Observables]") { } } -#ifndef _MSC_VER template void testHermitianObsShotVar() { if constexpr (!std::is_same_v) { using StateVectorT = typename TypeList::Type; @@ -1097,7 +1092,6 @@ TEST_CASE("Var - HermitianObs Shot", "[MeasurementsBase][Observables]") { } } -#endif template void testTensorProdObsVarShot() { if constexpr (!std::is_same_v) { @@ -1148,7 +1142,6 @@ template void testTensorProdObsVarShot() { expected, static_cast(0.20))); } -#ifndef _MSC_VER DYNAMIC_SECTION("With Hermitian and NameObs" << StateVectorToName::name) { using MatrixT = std::vector; @@ -1198,7 +1191,6 @@ template void testTensorProdObsVarShot() { REQUIRE_THAT(result, Catch::Matchers::WithinRel( expected, static_cast(0.20))); } -#endif DYNAMIC_SECTION(" full wires with apply operations" << StateVectorToName::name) { @@ -1557,7 +1549,6 @@ template void testHamiltonianObsExpvalShot() { expected, static_cast(0.20))); } -#ifndef _MSC_VER DYNAMIC_SECTION("YHer" << StateVectorToName::name) { auto Y0 = std::make_shared>( "PauliY", std::vector{0}); @@ -1578,7 +1569,6 @@ template void testHamiltonianObsExpvalShot() { REQUIRE_THAT(res, Catch::Matchers::WithinRel( expected, static_cast(0.20))); } -#endif testHamiltonianObsExpvalShot(); } @@ -1638,7 +1628,6 @@ template void testHamiltonianObsVarShot() { expected, static_cast(0.20))); } -#ifndef _MSC_VER DYNAMIC_SECTION("YHer" << StateVectorToName::name) { using ComplexT = typename StateVectorT::ComplexT; auto Y0 = std::make_shared>( @@ -1667,7 +1656,6 @@ template void testHamiltonianObsVarShot() { REQUIRE_THAT(res, Catch::Matchers::WithinRel( expected, static_cast(0.20))); } -#endif testHamiltonianObsVarShot(); } diff --git a/pennylane_lightning/core/src/observables/tests/Test_Observables.cpp b/pennylane_lightning/core/src/observables/tests/Test_Observables.cpp index 30a5dd4edd..98f9b175f3 100644 --- a/pennylane_lightning/core/src/observables/tests/Test_Observables.cpp +++ b/pennylane_lightning/core/src/observables/tests/Test_Observables.cpp @@ -207,7 +207,6 @@ template void testHermitianObsBase() { REQUIRE(ob2 != ob3); } -#ifndef _MSC_VER DYNAMIC_SECTION("Failed to create a HermitianObs- " << StateVectorToName::name) { std::mt19937_64 re{1337}; @@ -227,7 +226,6 @@ template void testHermitianObsBase() { Catch::Matchers::Contains("The matrix passed to HermitianObs " "is not a Hermitian matrix.")); } -#endif testHermitianObsBase(); } diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index e08812ddc3..3ec896ddba 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -56,7 +56,7 @@ std::unordered_map priority_lib = { #ifdef __linux__ std::string getPath() { Dl_info dl_info; - auto flag = dladdr((const void *)getPath, &dl_info); + auto flag = dladdr((const void *)getPath, &dl_info); // codefactor[skip] PL_ABORT_IF(!flag, "Can't get the path to the shared library."); std::string path(dl_info.dli_fname); return path; @@ -64,7 +64,7 @@ std::string getPath() { #elif defined(_MSC_VER) std::string getPath() { char buffer[MAX_PATH]; - GetModuleFileName(nullptr, buffer, MAX_PATH); + GetModuleFileName(nullptr, buffer, MAX_PATH); // codefactor[skip] std::string fullPath(buffer); std::size_t pos = fullPath.find_last_of("\\/"); return fullPath.substr(0, pos); From 510567e8b0c73854c8bf8ef0f9ab6df3d026b630 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Fri, 22 Mar 2024 22:56:54 -0400 Subject: [PATCH 306/428] add noinspection CodeFactor --- pennylane_lightning/core/src/utils/UtilLinearAlg.hpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index 3ec896ddba..4e55c5ca9f 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -56,7 +56,8 @@ std::unordered_map priority_lib = { #ifdef __linux__ std::string getPath() { Dl_info dl_info; - auto flag = dladdr((const void *)getPath, &dl_info); // codefactor[skip] + auto flag = + dladdr((const void *)getPath, &dl_info); // noinspection CodeFactor PL_ABORT_IF(!flag, "Can't get the path to the shared library."); std::string path(dl_info.dli_fname); return path; @@ -64,7 +65,7 @@ std::string getPath() { #elif defined(_MSC_VER) std::string getPath() { char buffer[MAX_PATH]; - GetModuleFileName(nullptr, buffer, MAX_PATH); // codefactor[skip] + GetModuleFileName(nullptr, buffer, MAX_PATH); // noinspection CodeFactor std::string fullPath(buffer); std::size_t pos = fullPath.find_last_of("\\/"); return fullPath.substr(0, pos); From 18231b5d85c2e2c803981785d5b0c7d56fe710e8 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Fri, 22 Mar 2024 22:59:24 -0400 Subject: [PATCH 307/428] codefactor[skip] for LoadLibrary --- pennylane_lightning/core/src/utils/SharedLibLoader.hpp | 2 +- pennylane_lightning/core/src/utils/UtilLinearAlg.hpp | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/pennylane_lightning/core/src/utils/SharedLibLoader.hpp b/pennylane_lightning/core/src/utils/SharedLibLoader.hpp index 551351448f..70e8b60f38 100644 --- a/pennylane_lightning/core/src/utils/SharedLibLoader.hpp +++ b/pennylane_lightning/core/src/utils/SharedLibLoader.hpp @@ -54,7 +54,7 @@ class SharedLibLoader final { // This allows users to use pre-installed LAPACK package PL_ABORT_IF(!handle_, dlerror()); #elif defined(_MSC_VER) - handle_ = LoadLibrary(filename.c_str()); + handle_ = LoadLibrary(filename.c_str()); // codefactor[skip] PL_ABORT_IF(!handle_, std::to_string(GetLastError())); #endif } diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index 4e55c5ca9f..e08812ddc3 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -56,8 +56,7 @@ std::unordered_map priority_lib = { #ifdef __linux__ std::string getPath() { Dl_info dl_info; - auto flag = - dladdr((const void *)getPath, &dl_info); // noinspection CodeFactor + auto flag = dladdr((const void *)getPath, &dl_info); PL_ABORT_IF(!flag, "Can't get the path to the shared library."); std::string path(dl_info.dli_fname); return path; @@ -65,7 +64,7 @@ std::string getPath() { #elif defined(_MSC_VER) std::string getPath() { char buffer[MAX_PATH]; - GetModuleFileName(nullptr, buffer, MAX_PATH); // noinspection CodeFactor + GetModuleFileName(nullptr, buffer, MAX_PATH); std::string fullPath(buffer); std::size_t pos = fullPath.find_last_of("\\/"); return fullPath.substr(0, pos); From 1d8d450bbeffcf23e331a9f7564447ae5a2d154f Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Fri, 22 Mar 2024 23:21:17 -0400 Subject: [PATCH 308/428] install scipy with the py cmake can access --- CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 184e2d2938..a4dd91ee6f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -96,7 +96,11 @@ if(CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME MATCHES "Windows") endif() message(STATUS "${SCIPY_PATH}") if(NOT EXISTS ${SCIPY_PATH}) - message(FATAL_ERROR "Can't find the scipy.libs in the python package_sites.") + #install scipy with available python interpreter + execute_process( + COMMAND ${Python_EXECUTABLE} -m pip install scipy + RESULT_VARIABLE result + ) endif() configure_file(${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h) endif() From fdce2cdc15c92261ce9def4a578ed40b55c8d903 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Fri, 22 Mar 2024 23:30:36 -0400 Subject: [PATCH 309/428] tidy up code --- .../core/src/measurements/tests/Test_MeasurementsBase.cpp | 1 - pennylane_lightning/core/src/utils/SharedLibLoader.hpp | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pennylane_lightning/core/src/measurements/tests/Test_MeasurementsBase.cpp b/pennylane_lightning/core/src/measurements/tests/Test_MeasurementsBase.cpp index 1b723f7212..a559773a7f 100644 --- a/pennylane_lightning/core/src/measurements/tests/Test_MeasurementsBase.cpp +++ b/pennylane_lightning/core/src/measurements/tests/Test_MeasurementsBase.cpp @@ -1092,7 +1092,6 @@ TEST_CASE("Var - HermitianObs Shot", "[MeasurementsBase][Observables]") { } } - template void testTensorProdObsVarShot() { if constexpr (!std::is_same_v) { using StateVectorT = typename TypeList::Type; diff --git a/pennylane_lightning/core/src/utils/SharedLibLoader.hpp b/pennylane_lightning/core/src/utils/SharedLibLoader.hpp index 70e8b60f38..b82a6c8dd1 100644 --- a/pennylane_lightning/core/src/utils/SharedLibLoader.hpp +++ b/pennylane_lightning/core/src/utils/SharedLibLoader.hpp @@ -54,7 +54,9 @@ class SharedLibLoader final { // This allows users to use pre-installed LAPACK package PL_ABORT_IF(!handle_, dlerror()); #elif defined(_MSC_VER) - handle_ = LoadLibrary(filename.c_str()); // codefactor[skip] +#pragma warning(push, 0) + handle_ = LoadLibrary(filename.c_str()); +#pragma warning(pop) PL_ABORT_IF(!handle_, std::to_string(GetLastError())); #endif } From 8fa13671bd4e0b39559c33766c18c4523ecc6f11 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Fri, 22 Mar 2024 23:38:29 -0400 Subject: [PATCH 310/428] codefactor warning suppress --- pennylane_lightning/core/src/utils/SharedLibLoader.hpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pennylane_lightning/core/src/utils/SharedLibLoader.hpp b/pennylane_lightning/core/src/utils/SharedLibLoader.hpp index b82a6c8dd1..4d13f124a5 100644 --- a/pennylane_lightning/core/src/utils/SharedLibLoader.hpp +++ b/pennylane_lightning/core/src/utils/SharedLibLoader.hpp @@ -54,9 +54,8 @@ class SharedLibLoader final { // This allows users to use pre-installed LAPACK package PL_ABORT_IF(!handle_, dlerror()); #elif defined(_MSC_VER) -#pragma warning(push, 0) + // codefactor[skip] handle_ = LoadLibrary(filename.c_str()); -#pragma warning(pop) PL_ABORT_IF(!handle_, std::to_string(GetLastError())); #endif } From e1e18c0129f56bc8c0ba28b9c6dccfb6155167af Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Fri, 22 Mar 2024 23:58:08 -0400 Subject: [PATCH 311/428] cmake call avail python to get sites_package --- CMakeLists.txt | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a4dd91ee6f..96baa6ef77 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -83,16 +83,20 @@ include(FetchContent) find_package(Python COMPONENTS Interpreter Development) -message("Python site-packages directory: ${Python_SITELIB}") - if(CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME MATCHES "Windows") - set(SCIPY_PATH ${Python_SITELIB}) + execute_process( + COMMAND ${Python_EXECUTABLE} -c "import site; print( f'{site.getsitepackages()[0]}')" + OUTPUT_VARIABLE PYTHON_PACKAGE_SITE + ) + set(SCIPY_PATH ${PYTHON_PACKAGE_SITE}) + message("Python site-packages directory: ${SCIPY_PATH}") + #set(SCIPY_PATH ${Python_SITELIB}) if(CMAKE_SYSTEM_NAME MATCHES "Linux") string(APPEND SCIPY_PATH "/scipy.libs") else() string(REPLACE "\\" "/" SCIPY_PATH ${SCIPY_PATH}) - string(APPEND SCIPY_PATH "/scipy.libs") + string(APPEND SCIPY_PATH "/Lib/site-packages/scipy.libs") endif() message(STATUS "${SCIPY_PATH}") if(NOT EXISTS ${SCIPY_PATH}) From 0b998e5cd661a71dd9378051ef6fbb401c3cf5f5 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Sat, 23 Mar 2024 00:08:11 -0400 Subject: [PATCH 312/428] test --- CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 96baa6ef77..e155cc75e5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -98,13 +98,14 @@ if(CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME MATCHES "Windows") string(REPLACE "\\" "/" SCIPY_PATH ${SCIPY_PATH}) string(APPEND SCIPY_PATH "/Lib/site-packages/scipy.libs") endif() - message(STATUS "${SCIPY_PATH}") + message(STATUS "${SCIPY_PATH}++++++++++++++") if(NOT EXISTS ${SCIPY_PATH}) #install scipy with available python interpreter execute_process( COMMAND ${Python_EXECUTABLE} -m pip install scipy RESULT_VARIABLE result ) + message("${result}----------------") endif() configure_file(${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h) endif() From ca84afa11bc1249dc19e53992d089a0517a64149 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Sat, 23 Mar 2024 08:38:47 -0400 Subject: [PATCH 313/428] debug windows --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e155cc75e5..e32b6eb732 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -98,7 +98,7 @@ if(CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME MATCHES "Windows") string(REPLACE "\\" "/" SCIPY_PATH ${SCIPY_PATH}) string(APPEND SCIPY_PATH "/Lib/site-packages/scipy.libs") endif() - message(STATUS "${SCIPY_PATH}++++++++++++++") + message(STATUS "++++++++${SCIPY_PATH}++++++++++++++") if(NOT EXISTS ${SCIPY_PATH}) #install scipy with available python interpreter execute_process( From db8bb3743f66282000ddb94874470f34243ae880 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Sat, 23 Mar 2024 08:50:45 -0400 Subject: [PATCH 314/428] strip traling whitespace execute_process --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e32b6eb732..f492401275 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -87,7 +87,7 @@ find_package(Python COMPONENTS Interpreter Development) if(CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME MATCHES "Windows") execute_process( COMMAND ${Python_EXECUTABLE} -c "import site; print( f'{site.getsitepackages()[0]}')" - OUTPUT_VARIABLE PYTHON_PACKAGE_SITE + OUTPUT_VARIABLE PYTHON_PACKAGE_SITE OUTPUT_STRIP_TRAILING_WHITESPACE ) set(SCIPY_PATH ${PYTHON_PACKAGE_SITE}) message("Python site-packages directory: ${SCIPY_PATH}") From ae7f517580469aa13a6fe45b78a954ef86d736a3 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Sat, 23 Mar 2024 09:03:22 -0400 Subject: [PATCH 315/428] suppress compile time check warning --- pennylane_lightning/core/src/utils/SharedLibLoader.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pennylane_lightning/core/src/utils/SharedLibLoader.hpp b/pennylane_lightning/core/src/utils/SharedLibLoader.hpp index 4d13f124a5..3f6258a3fc 100644 --- a/pennylane_lightning/core/src/utils/SharedLibLoader.hpp +++ b/pennylane_lightning/core/src/utils/SharedLibLoader.hpp @@ -32,6 +32,9 @@ namespace Pennylane::Util { */ // Adapted from Catalyst // (https://github.com/PennyLaneAI/catalyst/blob/f016a31f69d1b8a84bc9612af1bc64f0575506e9/runtime/lib/capi/ExecutionContext.hpp#L75) + +// Ignore invalid warnings for compile-time checks without kernel specifics +// NOLINTBEGIN class SharedLibLoader final { private: #if defined(__APPLE__) || defined(__linux__) @@ -81,5 +84,6 @@ class SharedLibLoader final { return sym; } }; +// NOLINTEND } // namespace Pennylane::Util \ No newline at end of file From bec4b4e0e1b98821f803c15dbf89bb4243e9d5d2 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Sat, 23 Mar 2024 09:03:45 -0400 Subject: [PATCH 316/428] fix typo --- pennylane_lightning/core/src/utils/SharedLibLoader.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/src/utils/SharedLibLoader.hpp b/pennylane_lightning/core/src/utils/SharedLibLoader.hpp index 3f6258a3fc..a846edf463 100644 --- a/pennylane_lightning/core/src/utils/SharedLibLoader.hpp +++ b/pennylane_lightning/core/src/utils/SharedLibLoader.hpp @@ -33,7 +33,7 @@ namespace Pennylane::Util { // Adapted from Catalyst // (https://github.com/PennyLaneAI/catalyst/blob/f016a31f69d1b8a84bc9612af1bc64f0575506e9/runtime/lib/capi/ExecutionContext.hpp#L75) -// Ignore invalid warnings for compile-time checks without kernel specifics +// Ignore invalid warnings for compile-time checks // NOLINTBEGIN class SharedLibLoader final { private: From 3f9b6a5850d66a5a73c37b9e4df0a7efcb3ae25e Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Sat, 23 Mar 2024 11:05:07 -0400 Subject: [PATCH 317/428] install scipy into local usr package sites --- CMakeLists.txt | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f492401275..6bd7c91acd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -98,14 +98,25 @@ if(CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME MATCHES "Windows") string(REPLACE "\\" "/" SCIPY_PATH ${SCIPY_PATH}) string(APPEND SCIPY_PATH "/Lib/site-packages/scipy.libs") endif() - message(STATUS "++++++++${SCIPY_PATH}++++++++++++++") - if(NOT EXISTS ${SCIPY_PATH}) + message(STATUS "Scipy directory: ${SCIPY_PATH}") + if(EXISTS ${SCIPY_PATH}) + message(STATUS "Scipy directory found.") + else() #install scipy with available python interpreter + message(STATUS "Can't find scipy with available python and install.") execute_process( - COMMAND ${Python_EXECUTABLE} -m pip install scipy + COMMAND ${Python_EXECUTABLE} -m pip install scipy --user RESULT_VARIABLE result ) - message("${result}----------------") + set(PYTHON_LOCAL_SITE_PACKAGES "~/.local/lib/python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}/site-packages") + string(APPEND PYTHON_LOCAL_SITE_PACKAGES "/scipy.libs") + set(SCIPY_PATH ${PYTHON_LOCAL_SITE_PACKAGES}) + if(EXISTS ${SCIPY_PATH}) + message(STATUS "Scipy directory found in local site_packages dir.") + else() + message(FATAL_ERROR "Scipy is not found.") + endif() + endif() configure_file(${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h) endif() From f71de3bbc939c2ae7d1e31028ce2a4d22500ff3b Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Sat, 23 Mar 2024 12:01:01 -0400 Subject: [PATCH 318/428] get scipy path directly instead of site-packages --- CMakeLists.txt | 72 +++++++++++-------- .../core/src/utils/config.h.in | 2 +- 2 files changed, 43 insertions(+), 31 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6bd7c91acd..d542e49bea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -83,41 +83,53 @@ include(FetchContent) find_package(Python COMPONENTS Interpreter Development) - if(CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME MATCHES "Windows") execute_process( - COMMAND ${Python_EXECUTABLE} -c "import site; print( f'{site.getsitepackages()[0]}')" - OUTPUT_VARIABLE PYTHON_PACKAGE_SITE OUTPUT_STRIP_TRAILING_WHITESPACE + COMMAND ${Python_EXECUTABLE} -c "import scipy; print(scipy.__file__)" + OUTPUT_VARIABLE SCIPY_INIT_FILE OUTPUT_STRIP_TRAILING_WHITESPACE ) - set(SCIPY_PATH ${PYTHON_PACKAGE_SITE}) - message("Python site-packages directory: ${SCIPY_PATH}") - #set(SCIPY_PATH ${Python_SITELIB}) - if(CMAKE_SYSTEM_NAME MATCHES "Linux") - string(APPEND SCIPY_PATH "/scipy.libs") - else() - string(REPLACE "\\" "/" SCIPY_PATH ${SCIPY_PATH}) - string(APPEND SCIPY_PATH "/Lib/site-packages/scipy.libs") - endif() - message(STATUS "Scipy directory: ${SCIPY_PATH}") - if(EXISTS ${SCIPY_PATH}) - message(STATUS "Scipy directory found.") + get_filename_component(SCIPY_PYTHON_DIR "${SCIPY_INIT_FILE}" DIRECTORY) # Get the directory containing __init__.py + set(SCIPY_LIBS_DIR ${SCIPY_PYTHON_DIR}/../scipy.libs) + message(STATUS "${SCIPY_PYTHON_DIR}") + if(EXISTS ${SCIPY_LIBS_DIR}) + message(STATUS "Scipy.libs directory found: ${SCIPY_LIBS_DIR}") else() - #install scipy with available python interpreter - message(STATUS "Can't find scipy with available python and install.") - execute_process( - COMMAND ${Python_EXECUTABLE} -m pip install scipy --user - RESULT_VARIABLE result - ) - set(PYTHON_LOCAL_SITE_PACKAGES "~/.local/lib/python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}/site-packages") - string(APPEND PYTHON_LOCAL_SITE_PACKAGES "/scipy.libs") - set(SCIPY_PATH ${PYTHON_LOCAL_SITE_PACKAGES}) - if(EXISTS ${SCIPY_PATH}) - message(STATUS "Scipy directory found in local site_packages dir.") - else() - message(FATAL_ERROR "Scipy is not found.") - endif() - + message(FATAL_ERROR "Scipy.libs directory not found.") endif() +#if(CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME MATCHES "Windows") +# execute_process( +# COMMAND ${Python_EXECUTABLE} -c "import site; print( f'{site.getsitepackages()[0]}')" +# OUTPUT_VARIABLE PYTHON_PACKAGE_SITE OUTPUT_STRIP_TRAILING_WHITESPACE +# ) +# set(SCIPY_PATH ${PYTHON_PACKAGE_SITE}) +# message("Python site-packages directory: ${SCIPY_PATH}") +# #set(SCIPY_PATH ${Python_SITELIB}) +# if(CMAKE_SYSTEM_NAME MATCHES "Linux") +# string(APPEND SCIPY_PATH "/scipy.libs") +# else() +# string(REPLACE "\\" "/" SCIPY_PATH ${SCIPY_PATH}) +# string(APPEND SCIPY_PATH "/Lib/site-packages/scipy.libs") +# endif() +# message(STATUS "Scipy directory: ${SCIPY_PATH}") +# if(EXISTS ${SCIPY_PATH}) +# message(STATUS "Scipy directory found.") +# else() +# #install scipy with available python interpreter +# message(STATUS "Can't find scipy with available python and install.") +# execute_process( +# COMMAND ${Python_EXECUTABLE} -m pip install scipy --user +# RESULT_VARIABLE result +# ) +# set(PYTHON_LOCAL_SITE_PACKAGES "~/.local/lib/python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}/site-packages") +# string(APPEND PYTHON_LOCAL_SITE_PACKAGES "/scipy.libs") +# set(SCIPY_PATH ${PYTHON_LOCAL_SITE_PACKAGES}) +# if(EXISTS ${SCIPY_PATH}) +# message(STATUS "Scipy directory found in local site_packages dir.") +# else() +# message(FATAL_ERROR "Scipy is not found.") +# endif() +# +# endif() configure_file(${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h) endif() diff --git a/pennylane_lightning/core/src/utils/config.h.in b/pennylane_lightning/core/src/utils/config.h.in index 0c4249e705..a13c1022d9 100644 --- a/pennylane_lightning/core/src/utils/config.h.in +++ b/pennylane_lightning/core/src/utils/config.h.in @@ -1,4 +1,4 @@ #ifndef CONFIG_H #define CONFIG_H -#define SCIPY_LIBS_PATH "${SCIPY_PATH}" +#define SCIPY_LIBS_PATH "${SCIPY_LIBS_DIR}" #endif \ No newline at end of file From 8c1d354209eb0e3db1d78f97c91d7ef451de72a6 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Sat, 23 Mar 2024 12:32:30 -0400 Subject: [PATCH 319/428] add python_sitelib for scipy search --- CMakeLists.txt | 42 +++++++----------------------------------- 1 file changed, 7 insertions(+), 35 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d542e49bea..fa18101705 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -94,42 +94,14 @@ if(CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME MATCHES "Windows") if(EXISTS ${SCIPY_LIBS_DIR}) message(STATUS "Scipy.libs directory found: ${SCIPY_LIBS_DIR}") else() - message(FATAL_ERROR "Scipy.libs directory not found.") + set(SCIPY_LIBS_DIR ${Python_SITELIB}/scipy.libs) + if(EXISTS ${SCIPY_LIBS_DIR}) + message(STATUS "Scipy.libs directory found: ${SCIPY_LIBS_DIR}") + else() + message(FATAL_ERROR "Scipy.libs directory not found.") + endif() endif() -#if(CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME MATCHES "Windows") -# execute_process( -# COMMAND ${Python_EXECUTABLE} -c "import site; print( f'{site.getsitepackages()[0]}')" -# OUTPUT_VARIABLE PYTHON_PACKAGE_SITE OUTPUT_STRIP_TRAILING_WHITESPACE -# ) -# set(SCIPY_PATH ${PYTHON_PACKAGE_SITE}) -# message("Python site-packages directory: ${SCIPY_PATH}") -# #set(SCIPY_PATH ${Python_SITELIB}) -# if(CMAKE_SYSTEM_NAME MATCHES "Linux") -# string(APPEND SCIPY_PATH "/scipy.libs") -# else() -# string(REPLACE "\\" "/" SCIPY_PATH ${SCIPY_PATH}) -# string(APPEND SCIPY_PATH "/Lib/site-packages/scipy.libs") -# endif() -# message(STATUS "Scipy directory: ${SCIPY_PATH}") -# if(EXISTS ${SCIPY_PATH}) -# message(STATUS "Scipy directory found.") -# else() -# #install scipy with available python interpreter -# message(STATUS "Can't find scipy with available python and install.") -# execute_process( -# COMMAND ${Python_EXECUTABLE} -m pip install scipy --user -# RESULT_VARIABLE result -# ) -# set(PYTHON_LOCAL_SITE_PACKAGES "~/.local/lib/python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}/site-packages") -# string(APPEND PYTHON_LOCAL_SITE_PACKAGES "/scipy.libs") -# set(SCIPY_PATH ${PYTHON_LOCAL_SITE_PACKAGES}) -# if(EXISTS ${SCIPY_PATH}) -# message(STATUS "Scipy directory found in local site_packages dir.") -# else() -# message(FATAL_ERROR "Scipy is not found.") -# endif() -# -# endif() + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h) endif() From c506395c6cb340757b5839828df7937f977a4822 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Sat, 23 Mar 2024 13:16:18 -0400 Subject: [PATCH 320/428] Trigger CI From b8e78400dd9f457de0d3e08b471bee88f272c9c0 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Sat, 23 Mar 2024 13:24:29 -0400 Subject: [PATCH 321/428] add scipy dependency for wheel building --- .github/workflows/wheel_linux_aarch64.yml | 2 +- .github/workflows/wheel_linux_ppc64le.yml | 2 +- .github/workflows/wheel_linux_x86_64.yml | 2 +- .github/workflows/wheel_linux_x86_64_cuda.yml | 2 +- .github/workflows/wheel_macos_arm64.yml | 2 +- .github/workflows/wheel_macos_x86_64.yml | 2 +- .github/workflows/wheel_win_x86_64.yml | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/wheel_linux_aarch64.yml b/.github/workflows/wheel_linux_aarch64.yml index 1a10b46a30..5ac0900107 100644 --- a/.github/workflows/wheel_linux_aarch64.yml +++ b/.github/workflows/wheel_linux_aarch64.yml @@ -130,7 +130,7 @@ jobs: # Python build settings CIBW_BEFORE_BUILD: | cat /etc/yum.conf | sed "s/\[main\]/\[main\]\ntimeout=5/g" > /etc/yum.conf - python -m pip install ninja cmake~=3.24.0 + python -m pip install ninja cmake~=3.24.0 scipy CIBW_ENVIRONMENT: | PL_BACKEND="${{ matrix.pl_backend }}" diff --git a/.github/workflows/wheel_linux_ppc64le.yml b/.github/workflows/wheel_linux_ppc64le.yml index 3d94bf906e..ab422001e8 100644 --- a/.github/workflows/wheel_linux_ppc64le.yml +++ b/.github/workflows/wheel_linux_ppc64le.yml @@ -129,7 +129,7 @@ jobs: # Python build settings CIBW_BEFORE_BUILD: | cat /etc/yum.conf | sed "s/\[main\]/\[main\]\ntimeout=5/g" > /etc/yum.conf - python -m pip install ninja cmake~=3.24.0 + python -m pip install ninja cmake~=3.24.0 scipy CIBW_ENVIRONMENT: | PL_BACKEND="${{ matrix.pl_backend }}" diff --git a/.github/workflows/wheel_linux_x86_64.yml b/.github/workflows/wheel_linux_x86_64.yml index 9101665494..888ae4a5fa 100644 --- a/.github/workflows/wheel_linux_x86_64.yml +++ b/.github/workflows/wheel_linux_x86_64.yml @@ -140,7 +140,7 @@ jobs: # Python build settings CIBW_BEFORE_BUILD: | cat /etc/yum.conf | sed "s/\[main\]/\[main\]\ntimeout=5/g" > /etc/yum.conf - python -m pip install ninja cmake~=3.24.0 + python -m pip install ninja cmake~=3.24.0 scipy yum clean all -y yum install centos-release-scl-rh -y yum install devtoolset-11-gcc-c++ -y diff --git a/.github/workflows/wheel_linux_x86_64_cuda.yml b/.github/workflows/wheel_linux_x86_64_cuda.yml index f537e53b31..f92916842e 100644 --- a/.github/workflows/wheel_linux_x86_64_cuda.yml +++ b/.github/workflows/wheel_linux_x86_64_cuda.yml @@ -71,7 +71,7 @@ jobs: # Python build settings CIBW_BEFORE_BUILD: | cat /etc/yum.conf | sed "s/\[main\]/\[main\]\ntimeout=5/g" > /etc/yum.conf - python -m pip install ninja cmake~=3.24.3 auditwheel~=5.0 custatevec-cu${{ matrix.cuda_version }} + python -m pip install ninja cmake~=3.24.3 auditwheel~=5.0 custatevec-cu${{ matrix.cuda_version }} scipy yum clean all -y yum install centos-release-scl-rh -y yum install devtoolset-11-gcc-c++ -y diff --git a/.github/workflows/wheel_macos_arm64.yml b/.github/workflows/wheel_macos_arm64.yml index 1d5e9f097a..1872c25d76 100644 --- a/.github/workflows/wheel_macos_arm64.yml +++ b/.github/workflows/wheel_macos_arm64.yml @@ -84,7 +84,7 @@ jobs: # Python build settings CIBW_BEFORE_BUILD: | - python -m pip install pybind11 ninja cmake~=3.24.0 setuptools + python -m pip install pybind11 ninja cmake~=3.24.0 setuptools scipy CIBW_ENVIRONMENT: | CMAKE_ARGS="-DCMAKE_CXX_COMPILER_TARGET=arm64-apple-macos11 -DCMAKE_SYSTEM_NAME=Darwin -DCMAKE_SYSTEM_PROCESSOR=ARM64 -DENABLE_OPENMP=OFF" \ diff --git a/.github/workflows/wheel_macos_x86_64.yml b/.github/workflows/wheel_macos_x86_64.yml index 7bcf05a793..f43165ef65 100644 --- a/.github/workflows/wheel_macos_x86_64.yml +++ b/.github/workflows/wheel_macos_x86_64.yml @@ -134,7 +134,7 @@ jobs: # Python build settings CIBW_BEFORE_BUILD: | - python -m pip install pybind11 ninja cmake~=3.24.0 setuptools + python -m pip install pybind11 ninja cmake~=3.24.0 setuptools scipy PL_BACKEND: ${{ matrix.pl_backend }} diff --git a/.github/workflows/wheel_win_x86_64.yml b/.github/workflows/wheel_win_x86_64.yml index 88a9feba10..de44d6d638 100644 --- a/.github/workflows/wheel_win_x86_64.yml +++ b/.github/workflows/wheel_win_x86_64.yml @@ -125,7 +125,7 @@ jobs: # Python build settings CIBW_BEFORE_BUILD: | - python -m pip install pybind11 cmake~=3.24.0 build + python -m pip install pybind11 cmake~=3.24.0 build scipy #Temporarily commenting while solving problems to find binaries in CIBW tests. # CIBW_BEFORE_TEST: | From df270002a63b09b183d674b4da4caab0865cdf53 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Sat, 23 Mar 2024 13:39:48 -0400 Subject: [PATCH 322/428] test for win wheel build --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fa18101705..9e6c4af31e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -98,7 +98,7 @@ if(CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME MATCHES "Windows") if(EXISTS ${SCIPY_LIBS_DIR}) message(STATUS "Scipy.libs directory found: ${SCIPY_LIBS_DIR}") else() - message(FATAL_ERROR "Scipy.libs directory not found.") + message(FATAL_ERROR "Scipy.libs directory (${SCIPY_LIBS_DIR}) not found.") endif() endif() From cf9e522917aa990c4848f87477294ede2ef50041 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Sat, 23 Mar 2024 13:55:42 -0400 Subject: [PATCH 323/428] windows wheel fix test --- CMakeLists.txt | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e6c4af31e..d7807d41e6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -98,7 +98,16 @@ if(CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME MATCHES "Windows") if(EXISTS ${SCIPY_LIBS_DIR}) message(STATUS "Scipy.libs directory found: ${SCIPY_LIBS_DIR}") else() - message(FATAL_ERROR "Scipy.libs directory (${SCIPY_LIBS_DIR}) not found.") + if(CMAKE_SYSTEM_NAME MATCHES "Windows") + set(SCIPY_LIBS_DIR ${Python_SITELIB}) + string(REPLACE "\\" "/" SCIPY_LIBS_DIR ${SCIPY_LIBS_DIR}) + set(SCIPY_LIBS_DIR ${SCIPY_LIBS_DIR}/scipy.libs) + if(NOT EXISTS ${SCIPY_LIBS_DIR}) + message(FATAL_ERROR "Scipy.libs directory (${SCIPY_LIBS_DIR}) not found.") + endif() + else() + message(FATAL_ERROR "Scipy.libs directory (${SCIPY_LIBS_DIR}) not found.") + endif() endif() endif() From 450353d7b676ecef575373fb82074a3ba526be94 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Sat, 23 Mar 2024 14:06:59 -0400 Subject: [PATCH 324/428] install scipy with cmake --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index d7807d41e6..1f7f4e4a21 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -84,6 +84,8 @@ include(FetchContent) find_package(Python COMPONENTS Interpreter Development) if(CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME MATCHES "Windows") + execute_process(COMMAND ${Python_EXECUTABLE} -m pip install scipy) + execute_process( COMMAND ${Python_EXECUTABLE} -c "import scipy; print(scipy.__file__)" OUTPUT_VARIABLE SCIPY_INIT_FILE OUTPUT_STRIP_TRAILING_WHITESPACE From 0d3db1bba43694c2994100e22d082ae9210a4ea9 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Sat, 23 Mar 2024 14:18:07 -0400 Subject: [PATCH 325/428] win wheels --- .github/workflows/wheel_win_x86_64.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/wheel_win_x86_64.yml b/.github/workflows/wheel_win_x86_64.yml index de44d6d638..669a398158 100644 --- a/.github/workflows/wheel_win_x86_64.yml +++ b/.github/workflows/wheel_win_x86_64.yml @@ -115,7 +115,7 @@ jobs: -Destination "D:\a\Lightning-Unification\Lightning-Unification\Kokkos" -Recurse -Force - name: Install cibuildwheel - run: python -m pip install cibuildwheel~=2.16.0 wheel + run: python -m pip install cibuildwheel~=2.16.0 wheel scipy - name: Build wheels env: From 3858f20f1603457e7a3d973d1b1d3af7e57b57f1 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Sat, 23 Mar 2024 21:53:43 -0400 Subject: [PATCH 326/428] fix multiple python version in win wheels build --- .github/workflows/wheel_win_x86_64.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/wheel_win_x86_64.yml b/.github/workflows/wheel_win_x86_64.yml index 669a398158..bb4292a3e7 100644 --- a/.github/workflows/wheel_win_x86_64.yml +++ b/.github/workflows/wheel_win_x86_64.yml @@ -98,6 +98,11 @@ jobs: runs-on: ${{ matrix.os }} steps: + - uses: actions/setup-python@v4 + name: Install Python + with: + python-version: '3.9' + - name: Restoring cached dependencies id: kokkos-cache uses: actions/cache@v3 @@ -115,7 +120,7 @@ jobs: -Destination "D:\a\Lightning-Unification\Lightning-Unification\Kokkos" -Recurse -Force - name: Install cibuildwheel - run: python -m pip install cibuildwheel~=2.16.0 wheel scipy + run: python -m pip install cibuildwheel~=2.16.0 wheel - name: Build wheels env: From 1ab88c0a531feea65e6432c4f10aa68a55ae2e75 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Sat, 23 Mar 2024 22:00:36 -0400 Subject: [PATCH 327/428] debug win wheels build --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1f7f4e4a21..2bb8c146e0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -84,7 +84,7 @@ include(FetchContent) find_package(Python COMPONENTS Interpreter Development) if(CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME MATCHES "Windows") - execute_process(COMMAND ${Python_EXECUTABLE} -m pip install scipy) + #execute_process(COMMAND ${Python_EXECUTABLE} -m pip install scipy) execute_process( COMMAND ${Python_EXECUTABLE} -c "import scipy; print(scipy.__file__)" From 787a90143fd2e6c096f76921cd49fd2b66fd40c5 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Sat, 23 Mar 2024 22:37:42 -0400 Subject: [PATCH 328/428] abs path search for cpp backend, rpath for python --- CMakeLists.txt | 52 ++++++++----------- .../core/src/utils/CMakeLists.txt | 4 ++ .../core/src/utils/UtilLinearAlg.hpp | 21 ++++---- 3 files changed, 38 insertions(+), 39 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2bb8c146e0..8629a3fb61 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -83,45 +83,37 @@ include(FetchContent) find_package(Python COMPONENTS Interpreter Development) -if(CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME MATCHES "Windows") - #execute_process(COMMAND ${Python_EXECUTABLE} -m pip install scipy) - - execute_process( - COMMAND ${Python_EXECUTABLE} -c "import scipy; print(scipy.__file__)" - OUTPUT_VARIABLE SCIPY_INIT_FILE OUTPUT_STRIP_TRAILING_WHITESPACE +if(ENABLE_PYTHON) + FetchContent_Declare(pybind11 + GIT_REPOSITORY https://github.com/pybind/pybind11.git + GIT_TAG v2.11.1 ) - get_filename_component(SCIPY_PYTHON_DIR "${SCIPY_INIT_FILE}" DIRECTORY) # Get the directory containing __init__.py - set(SCIPY_LIBS_DIR ${SCIPY_PYTHON_DIR}/../scipy.libs) - message(STATUS "${SCIPY_PYTHON_DIR}") - if(EXISTS ${SCIPY_LIBS_DIR}) - message(STATUS "Scipy.libs directory found: ${SCIPY_LIBS_DIR}") - else() - set(SCIPY_LIBS_DIR ${Python_SITELIB}/scipy.libs) + FetchContent_MakeAvailable(pybind11) +else() + #For direct cpp backend usage. + if(CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME MATCHES "Windows") + execute_process( + COMMAND ${Python_EXECUTABLE} -c "import scipy; print(scipy.__file__)" + OUTPUT_VARIABLE SCIPY_INIT_FILE OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + get_filename_component(SCIPY_PYTHON_DIR "${SCIPY_INIT_FILE}" DIRECTORY) # Get the directory containing __init__.py + set(SCIPY_LIBS_DIR ${SCIPY_PYTHON_DIR}/../scipy.libs) + message(STATUS "${SCIPY_PYTHON_DIR}") + if(EXISTS ${SCIPY_LIBS_DIR}) message(STATUS "Scipy.libs directory found: ${SCIPY_LIBS_DIR}") else() - if(CMAKE_SYSTEM_NAME MATCHES "Windows") - set(SCIPY_LIBS_DIR ${Python_SITELIB}) - string(REPLACE "\\" "/" SCIPY_LIBS_DIR ${SCIPY_LIBS_DIR}) - set(SCIPY_LIBS_DIR ${SCIPY_LIBS_DIR}/scipy.libs) - if(NOT EXISTS ${SCIPY_LIBS_DIR}) - message(FATAL_ERROR "Scipy.libs directory (${SCIPY_LIBS_DIR}) not found.") - endif() + set(SCIPY_LIBS_DIR ${Python_SITELIB}/scipy.libs) + if(EXISTS ${SCIPY_LIBS_DIR}) + message(STATUS "Scipy.libs directory found: ${SCIPY_LIBS_DIR}") else() message(FATAL_ERROR "Scipy.libs directory (${SCIPY_LIBS_DIR}) not found.") endif() endif() - endif() - - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h) - endif() -if(ENABLE_PYTHON) - FetchContent_Declare(pybind11 - GIT_REPOSITORY https://github.com/pybind/pybind11.git - GIT_TAG v2.11.1 - ) - FetchContent_MakeAvailable(pybind11) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h) + endif() endif() set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) diff --git a/pennylane_lightning/core/src/utils/CMakeLists.txt b/pennylane_lightning/core/src/utils/CMakeLists.txt index d17412e80d..a8a8b7ef5c 100644 --- a/pennylane_lightning/core/src/utils/CMakeLists.txt +++ b/pennylane_lightning/core/src/utils/CMakeLists.txt @@ -7,6 +7,10 @@ set(LQUBIT_UTILS_FILES RuntimeInfo.cpp UtilLinearAlg.cpp CACHE INTERNAL "" FORCE add_library(lightning_utils STATIC ${LQUBIT_UTILS_FILES}) +if(ENABLE_PYTHON) + target_compile_options(lightning_compile_options INTERFACE "-D_ENABLE_PYTHON=1") +endif() + target_include_directories(lightning_utils INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(lightning_utils INTERFACE lightning_compile_options lightning_external_libs diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index e08812ddc3..f83b57ee7b 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -34,9 +34,11 @@ #include #endif +#ifndef _ENABLE_PYTHON #ifndef __APPLE__ #include "config.h" #endif +#endif #include "SharedLibLoader.hpp" @@ -115,15 +117,16 @@ void compute_diagonalizing_gates(int n, int lda, std::shared_ptr blasLib; std::vector> blasLibs; - const std::string currentPathStr(SCIPY_LIBS_PATH); - std::filesystem::path scipyLibsPath; - - if (std::filesystem::exists(currentPathStr)) { - scipyLibsPath = currentPathStr; - } else { - std::filesystem::path currentPath(getPath()); - scipyLibsPath = currentPath.parent_path().parent_path() / "scipy.libs"; - } +#ifdef _ENABLE_PYTHON + std::filesystem::path currentPath(getPath()); + std::filesystem::path scipyLibsPath = + currentPath.parent_path().parent_path() / "scipy.libs"; +#else + const std::string scipyPathStr(SCIPY_LIBS_PATH); + PL_ABORT_IF(!std::filesystem::exists(scipyPathStr), + "scipy.libs is not available."); + std::filesystem::path scipyLibsPath = scipyPathStr; +#endif std::vector> availableLibs; From 8058c0aacd96e91021593c18052e8870e8c3ddde Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Sat, 23 Mar 2024 22:49:15 -0400 Subject: [PATCH 329/428] quick fix --- .github/workflows/tests_linux_x86_mpi_gpu.yml | 1 + pennylane_lightning/core/src/utils/UtilLinearAlg.hpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests_linux_x86_mpi_gpu.yml b/.github/workflows/tests_linux_x86_mpi_gpu.yml index 7a67ae0147..1fe9f74075 100644 --- a/.github/workflows/tests_linux_x86_mpi_gpu.yml +++ b/.github/workflows/tests_linux_x86_mpi_gpu.yml @@ -119,6 +119,7 @@ jobs: export CUQUANTUM_SDK=$(python -c "import site; print( f'{site.getsitepackages()[0]}/cuquantum/lib')") cmake . -BBuild \ -DPL_BACKEND=lightning_gpu \ + -DENABLE_PYTHON=OFF \ -DENABLE_MPI=ON \ -DCMAKE_BUILD_TYPE=Debug \ -DENABLE_COVERAGE=ON \ diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index f83b57ee7b..9dbe5ba9be 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -34,7 +34,7 @@ #include #endif -#ifndef _ENABLE_PYTHON +#if _ENABLE_PYTHON == 1 #ifndef __APPLE__ #include "config.h" #endif From 1f08d011ff32d553505e646a67dace101304ed21 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Sat, 23 Mar 2024 23:20:04 -0400 Subject: [PATCH 330/428] quick fix --- pennylane_lightning/core/src/utils/UtilLinearAlg.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index 9dbe5ba9be..f83b57ee7b 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -34,7 +34,7 @@ #include #endif -#if _ENABLE_PYTHON == 1 +#ifndef _ENABLE_PYTHON #ifndef __APPLE__ #include "config.h" #endif From aa73793eb403335a0d91ab8385a9b7fd6769b9af Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Sun, 24 Mar 2024 00:12:41 -0400 Subject: [PATCH 331/428] lapack path info stored in config.hpp for macos --- CMakeLists.txt | 9 +++++++++ Makefile | 1 + pennylane_lightning/core/src/utils/UtilLinearAlg.hpp | 8 ++++---- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8629a3fb61..d0644f5296 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -111,8 +111,17 @@ else() message(FATAL_ERROR "Scipy.libs directory (${SCIPY_LIBS_DIR}) not found.") endif() endif() + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h) + endif() +endif() +if(CMAKE_SYSTEM_NAME MATCHES "Darwin") + set(SCIPY_LIBS_DIR "/System/Library/Frameworks/Accelerate.framework/Versions/Current/Frameworks/vecLib.framework") + if(EXISTS ${SCIPY_LIBS_DIR}) + set(SCIPY_LIBS_DIR "${SCIPY_LIBS_DIR}/libLAPACK.dylib") configure_file(${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h) + else() + message(FATAL_ERROR "Apple BLAS library not found.") endif() endif() diff --git a/Makefile b/Makefile index 3d40597db9..f9e75fab82 100644 --- a/Makefile +++ b/Makefile @@ -96,6 +96,7 @@ test-cpp: rm -rf ./BuildTests cmake -BBuildTests -G Ninja \ -DCMAKE_BUILD_TYPE=Debug \ + -DENABLE_PYTHON=OFF \ -DBUILD_TESTS=ON \ -DENABLE_WARNINGS=ON \ -DPL_BACKEND=$(PL_BACKEND) \ diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index f83b57ee7b..931e5e32b2 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -35,9 +35,11 @@ #endif #ifndef _ENABLE_PYTHON -#ifndef __APPLE__ #include "config.h" #endif + +#ifdef __APPLE__ +#include "config.h" #endif #include "SharedLibLoader.hpp" @@ -107,9 +109,7 @@ void compute_diagonalizing_gates(int n, int lda, } } #ifdef __APPLE__ - const std::string libName = - "/System/Library/Frameworks/Accelerate.framework/Versions/Current/" - "Frameworks/vecLib.framework/libLAPACK.dylib"; + const std::string libName = SCIPY_LIBS_PATH; std::shared_ptr blasLib = std::make_shared(libName); #else From 2e16759f46d6491c743007e158d353c46fc14a48 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Sun, 24 Mar 2024 00:28:12 -0400 Subject: [PATCH 332/428] add a dummy config.h --- pennylane_lightning/core/src/utils/config.h | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 pennylane_lightning/core/src/utils/config.h diff --git a/pennylane_lightning/core/src/utils/config.h b/pennylane_lightning/core/src/utils/config.h new file mode 100644 index 0000000000..dc0eb1b345 --- /dev/null +++ b/pennylane_lightning/core/src/utils/config.h @@ -0,0 +1,4 @@ +#ifndef CONFIG_H +#define CONFIG_H +#define SCIPY_LIBS_PATH 0 +#endif From 4cb82a26617465f96a3b29572117595f4a9094bf Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Sun, 24 Mar 2024 00:59:28 -0400 Subject: [PATCH 333/428] fix getPath --- .../core/src/utils/UtilLinearAlg.hpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index 931e5e32b2..59fb3009f7 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -36,9 +36,7 @@ #ifndef _ENABLE_PYTHON #include "config.h" -#endif - -#ifdef __APPLE__ +#elif defined(__APPLE__) #include "config.h" #endif @@ -58,12 +56,19 @@ std::unordered_map priority_lib = { {"stdc", 0}, {"gcc", 1}, {"quadmath", 2}, {"gfortran", 3}, {"openblas", 4}}; #ifdef __linux__ -std::string getPath() { +/*std::string getPath() { Dl_info dl_info; auto flag = dladdr((const void *)getPath, &dl_info); PL_ABORT_IF(!flag, "Can't get the path to the shared library."); std::string path(dl_info.dli_fname); return path; +}*/ + +const char *getPath() { + Dl_info dl_info; + PL_ABORT_IF(dladdr((const void *)getPath, &dl_info) == 0, + "Can't get the path to the shared library."); + return dl_info.dli_fname; } #elif defined(_MSC_VER) std::string getPath() { From 20f2375ea5264c8a017cc0647c961c6a44b4a3a3 Mon Sep 17 00:00:00 2001 From: Dev version update bot Date: Mon, 25 Mar 2024 13:19:43 +0000 Subject: [PATCH 334/428] Auto update version --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index ec9ead0f50..0ae493ae06 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.36.0-dev18" +__version__ = "0.36.0-dev19" From 04a86abab9db898ce5b05d139ac75b0e83553149 Mon Sep 17 00:00:00 2001 From: "Lee J. O'Riordan" Date: Mon, 25 Mar 2024 09:20:23 -0400 Subject: [PATCH 335/428] Trigger CI From bf242121b0165515ac866d5e3e502fc6ee767582 Mon Sep 17 00:00:00 2001 From: "Lee J. O'Riordan" Date: Mon, 25 Mar 2024 09:27:29 -0400 Subject: [PATCH 336/428] Fix format --- tests/test_serialize.py | 90 ++++++++++++++++++++--------------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/tests/test_serialize.py b/tests/test_serialize.py index 7b29292a24..9accbe9739 100644 --- a/tests/test_serialize.py +++ b/tests/test_serialize.py @@ -254,12 +254,14 @@ def test_hamiltonian_return(self, use_csingle, wires_map): named_obs("PauliY", [2]), ] ), - hamiltonian_obs( - np.array([1], dtype=r_dtype), - [tensor_prod_obs([named_obs("PauliY", [2]), named_obs("PauliX", [0])])], - ) - if qml.operation.active_new_opmath() - else tensor_prod_obs([named_obs("PauliX", [0]), named_obs("PauliY", [2])]), + ( + hamiltonian_obs( + np.array([1], dtype=r_dtype), + [tensor_prod_obs([named_obs("PauliY", [2]), named_obs("PauliX", [0])])], + ) + if qml.operation.active_new_opmath() + else tensor_prod_obs([named_obs("PauliX", [0]), named_obs("PauliY", [2])]) + ), hermitian_obs(np.ones(64, dtype=c_dtype), [0, 1, 2]), ], ) @@ -391,45 +393,43 @@ def test_hamiltonian_mix_return(self, use_csingle, wires_map): s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables( tape, wires_map ) - - s_expected1 = hamiltonian_obs( - np.array([0.3, 0.5, 0.4], dtype=r_dtype), - [ - tensor_prod_obs( - [ - hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), - named_obs("PauliY", [2]), - ] - ), - hamiltonian_obs( - np.array([1], dtype=r_dtype), - [tensor_prod_obs([named_obs("PauliY", [2]), named_obs("PauliX", [0])])], - ) - if qml.operation.active_new_opmath() - else tensor_prod_obs([named_obs("PauliX", [0]), named_obs("PauliY", [2])]), - hermitian_obs(np.ones(64, dtype=c_dtype), [0, 1, 2]), - ], - ) - s_expected2 = hamiltonian_obs( - np.array([0.7, 0.3], dtype=r_dtype), - [ - tensor_prod_obs( - [ - named_obs("PauliX", [0]), - hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [1, 2]), - ] - ), - hamiltonian_obs( - np.array([1], dtype=r_dtype), - [tensor_prod_obs([named_obs("PauliX", [2]), named_obs("PauliY", [0])])], - ) - if qml.operation.active_new_opmath() - else tensor_prod_obs([named_obs("PauliY", [0]), named_obs("PauliX", [2])]), - ], - ) - hamiltonian_obs( - np.array([1], dtype=r_dtype), - [tensor_prod_obs([named_obs("PauliX", [2]), named_obs("PauliY", [0])])], + if qml.operation.active_new_opmath(): + s_expected1 = hamiltonian_obs( + np.array([0.3, 0.5, 0.4], dtype=r_dtype), + [ + tensor_prod_obs( + [ + hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [0, 1]), + named_obs("PauliY", [2]), + ] + ), + ( + hamiltonian_obs( + np.array([1], dtype=r_dtype), + [tensor_prod_obs([named_obs("PauliY", [2]), named_obs("PauliX", [0])])], + ) + if qml.operation.active_new_opmath() + else tensor_prod_obs([named_obs("PauliX", [0]), named_obs("PauliY", [2])]) + ), + hermitian_obs(np.ones(64, dtype=c_dtype), [0, 1, 2]), + ], + ) + s_expected2 = hamiltonian_obs( + np.array([0.7, 0.3], dtype=r_dtype), + [ + tensor_prod_obs( + [ + named_obs("PauliX", [0]), + hermitian_obs(np.eye(4, dtype=c_dtype).ravel(), [1, 2]), + ] + ), + ( + hamiltonian_obs( + np.array([1], dtype=r_dtype), + [tensor_prod_obs([named_obs("PauliX", [2]), named_obs("PauliY", [0])])], + ) + if qml.operation.active_new_opmath() + else tensor_prod_obs([named_obs("PauliY", [0]), named_obs("PauliX", [2])]) ), ], ) From 0e6480cbe964455023b94fd5ee191d3872046040 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Mon, 25 Mar 2024 10:01:14 -0400 Subject: [PATCH 337/428] get scipy path from env variable --- .github/workflows/tests_gpu_cuda.yml | 1 + .github/workflows/tests_gpu_kokkos.yml | 1 + .github/workflows/tests_linux.yml | 4 ++ .github/workflows/tests_linux_x86_mpi_gpu.yml | 1 + .github/workflows/tests_windows.yml | 2 + CMakeLists.txt | 37 +------------------ .../core/src/utils/UtilLinearAlg.hpp | 26 +++++-------- pennylane_lightning/core/src/utils/config.h | 4 -- .../core/src/utils/config.h.in | 4 -- 9 files changed, 20 insertions(+), 60 deletions(-) delete mode 100644 pennylane_lightning/core/src/utils/config.h delete mode 100644 pennylane_lightning/core/src/utils/config.h.in diff --git a/.github/workflows/tests_gpu_cuda.yml b/.github/workflows/tests_gpu_cuda.yml index f35dfa24a6..cfe5d74e0c 100644 --- a/.github/workflows/tests_gpu_cuda.yml +++ b/.github/workflows/tests_gpu_cuda.yml @@ -124,6 +124,7 @@ jobs: - name: Build and run unit tests run: | cd main + export SCIPY_LIBS=$(python -c "import site; print( f'{site.getsitepackages()[0]}/scipy.libs')") cmake . -BBuild \ -DCUQUANTUM_SDK=$(python -c "import site; print( f'{site.getsitepackages()[0]}/cuquantum')")\ -DBUILD_TESTS=ON \ diff --git a/.github/workflows/tests_gpu_kokkos.yml b/.github/workflows/tests_gpu_kokkos.yml index 674557093d..7cfdf51148 100644 --- a/.github/workflows/tests_gpu_kokkos.yml +++ b/.github/workflows/tests_gpu_kokkos.yml @@ -155,6 +155,7 @@ jobs: - name: Build and run unit tests run: | cd main + export SCIPY_LIBS=$(python -c "import site; print( f'{site.getsitepackages()[0]}/scipy.libs')") cmake . -BBuild -G Ninja \ -DCMAKE_BUILD_TYPE=Debug \ -DBUILD_TESTS=ON \ diff --git a/.github/workflows/tests_linux.yml b/.github/workflows/tests_linux.yml index ca7dc00118..5f06095389 100644 --- a/.github/workflows/tests_linux.yml +++ b/.github/workflows/tests_linux.yml @@ -55,6 +55,7 @@ jobs: - name: Build and run unit tests run: | set -x + export SCIPY_LIBS=$(python -c "import site; print( f'{site.getsitepackages()[0]}/scipy.libs')") cmake . -BBuild -G Ninja \ -DCMAKE_BUILD_TYPE=Debug \ -DBUILD_TESTS=ON \ @@ -229,6 +230,7 @@ jobs: - name: Build and run unit tests run: | set -x + export SCIPY_LIBS=$(python -c "import site; print( f'{site.getsitepackages()[0]}/scipy.libs')") cmake . -BBuild -G Ninja \ -DCMAKE_BUILD_TYPE=Debug \ -DENABLE_PYTHON=OFF \ @@ -411,6 +413,7 @@ jobs: - name: Build and run unit tests run: | set -x + export SCIPY_LIBS=$(python -c "import site; print( f'{site.getsitepackages()[0]}/scipy.libs')") cmake . -BBuild -G Ninja \ -DCMAKE_BUILD_TYPE=Debug \ -DBUILD_TESTS=ON \ @@ -677,6 +680,7 @@ jobs: - name: Build and run unit tests run: | set -x + export SCIPY_LIBS=$(python -c "import site; print( f'{site.getsitepackages()[0]}/scipy.libs')") cmake . -BBuild -G Ninja \ -DCMAKE_BUILD_TYPE=Debug \ -DBUILD_TESTS=ON \ diff --git a/.github/workflows/tests_linux_x86_mpi_gpu.yml b/.github/workflows/tests_linux_x86_mpi_gpu.yml index 1fe9f74075..445993bad1 100644 --- a/.github/workflows/tests_linux_x86_mpi_gpu.yml +++ b/.github/workflows/tests_linux_x86_mpi_gpu.yml @@ -117,6 +117,7 @@ jobs: run: | source /etc/profile.d/modules.sh && module use /opt/modules/ && module load ${{ matrix.mpilib }}/cuda-${{ matrix.cuda_version_maj }}.${{ matrix.cuda_version_min }} export CUQUANTUM_SDK=$(python -c "import site; print( f'{site.getsitepackages()[0]}/cuquantum/lib')") + export SCIPY_LIBS=$(python -c "import site; print( f'{site.getsitepackages()[0]}/scipy.libs')") cmake . -BBuild \ -DPL_BACKEND=lightning_gpu \ -DENABLE_PYTHON=OFF \ diff --git a/.github/workflows/tests_windows.yml b/.github/workflows/tests_windows.yml index 66bcce17ed..4467b716b0 100644 --- a/.github/workflows/tests_windows.yml +++ b/.github/workflows/tests_windows.yml @@ -122,6 +122,7 @@ jobs: - name: Build and run unit tests for code coverage run: | + python -c "import os; import site; os.environ['SCIPY_LIBS'] = f'{site.getsitepackages()[0]}/scipy.libs'" cmake -BBuild ` -DBUILD_TESTS=ON ` -DENABLE_OPENMP=OFF ` @@ -211,6 +212,7 @@ jobs: run: | Subst Z: (pwd) Set-Location -Path "Z:\" + python -c "import os; import site; os.environ['SCIPY_LIBS'] = f'{site.getsitepackages()[0]}/scipy.libs'" cmake -BBuild ` -DBUILD_TESTS=ON ` -DENABLE_PYTHON=OFF ` diff --git a/CMakeLists.txt b/CMakeLists.txt index d0644f5296..523a65da2f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -81,48 +81,13 @@ set(CMAKE_POLICY_DEFAULT_CMP0127 NEW) # To suppress pybind11 CMP0127 warning # Add pybind11 include(FetchContent) -find_package(Python COMPONENTS Interpreter Development) - if(ENABLE_PYTHON) + find_package(Python COMPONENTS Interpreter Development) FetchContent_Declare(pybind11 GIT_REPOSITORY https://github.com/pybind/pybind11.git GIT_TAG v2.11.1 ) FetchContent_MakeAvailable(pybind11) -else() - #For direct cpp backend usage. - if(CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME MATCHES "Windows") - execute_process( - COMMAND ${Python_EXECUTABLE} -c "import scipy; print(scipy.__file__)" - OUTPUT_VARIABLE SCIPY_INIT_FILE OUTPUT_STRIP_TRAILING_WHITESPACE - ) - - get_filename_component(SCIPY_PYTHON_DIR "${SCIPY_INIT_FILE}" DIRECTORY) # Get the directory containing __init__.py - set(SCIPY_LIBS_DIR ${SCIPY_PYTHON_DIR}/../scipy.libs) - message(STATUS "${SCIPY_PYTHON_DIR}") - - if(EXISTS ${SCIPY_LIBS_DIR}) - message(STATUS "Scipy.libs directory found: ${SCIPY_LIBS_DIR}") - else() - set(SCIPY_LIBS_DIR ${Python_SITELIB}/scipy.libs) - if(EXISTS ${SCIPY_LIBS_DIR}) - message(STATUS "Scipy.libs directory found: ${SCIPY_LIBS_DIR}") - else() - message(FATAL_ERROR "Scipy.libs directory (${SCIPY_LIBS_DIR}) not found.") - endif() - endif() - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h) - endif() -endif() - -if(CMAKE_SYSTEM_NAME MATCHES "Darwin") - set(SCIPY_LIBS_DIR "/System/Library/Frameworks/Accelerate.framework/Versions/Current/Frameworks/vecLib.framework") - if(EXISTS ${SCIPY_LIBS_DIR}) - set(SCIPY_LIBS_DIR "${SCIPY_LIBS_DIR}/libLAPACK.dylib") - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h) - else() - message(FATAL_ERROR "Apple BLAS library not found.") - endif() endif() set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index 59fb3009f7..3e26787f9c 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -30,16 +31,9 @@ #if defined(__APPLE__) || defined(__linux__) #include #elif defined(_MSC_VER) -#include #include #endif -#ifndef _ENABLE_PYTHON -#include "config.h" -#elif defined(__APPLE__) -#include "config.h" -#endif - #include "SharedLibLoader.hpp" /// @cond DEV @@ -114,24 +108,24 @@ void compute_diagonalizing_gates(int n, int lda, } } #ifdef __APPLE__ - const std::string libName = SCIPY_LIBS_PATH; + const std::string libName = + "/System/Library/Frameworks/Accelerate.framework/Versions/Current/" + "Frameworks/vecLib.framework/libLAPACK.dylib"; std::shared_ptr blasLib = std::make_shared(libName); #else - std::shared_ptr blasLib; std::vector> blasLibs; -#ifdef _ENABLE_PYTHON std::filesystem::path currentPath(getPath()); std::filesystem::path scipyLibsPath = currentPath.parent_path().parent_path() / "scipy.libs"; -#else - const std::string scipyPathStr(SCIPY_LIBS_PATH); - PL_ABORT_IF(!std::filesystem::exists(scipyPathStr), - "scipy.libs is not available."); - std::filesystem::path scipyLibsPath = scipyPathStr; -#endif + if (!std::filesystem::exists(scipyLibsPath)) { + const std::string scipyPathStr(std::getenv(SCIPY_LIBS)); + PL_ABORT_IF(!std::filesystem::exists(scipyPathStr), + "The SCIPY_LIBS env is not available."); + scipyLibsPath = scipyPathStr; + } std::vector> availableLibs; diff --git a/pennylane_lightning/core/src/utils/config.h b/pennylane_lightning/core/src/utils/config.h deleted file mode 100644 index dc0eb1b345..0000000000 --- a/pennylane_lightning/core/src/utils/config.h +++ /dev/null @@ -1,4 +0,0 @@ -#ifndef CONFIG_H -#define CONFIG_H -#define SCIPY_LIBS_PATH 0 -#endif diff --git a/pennylane_lightning/core/src/utils/config.h.in b/pennylane_lightning/core/src/utils/config.h.in deleted file mode 100644 index a13c1022d9..0000000000 --- a/pennylane_lightning/core/src/utils/config.h.in +++ /dev/null @@ -1,4 +0,0 @@ -#ifndef CONFIG_H -#define CONFIG_H -#define SCIPY_LIBS_PATH "${SCIPY_LIBS_DIR}" -#endif \ No newline at end of file From 73a7feabc70aee7c3a7b42620db104f1fb0ac699 Mon Sep 17 00:00:00 2001 From: Dev version update bot Date: Mon, 25 Mar 2024 14:01:35 +0000 Subject: [PATCH 338/428] Auto update version --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index ec9ead0f50..0ae493ae06 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.36.0-dev18" +__version__ = "0.36.0-dev19" From 3a3bed33fddc0cbde85311e820ea66e459b7134d Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Mon, 25 Mar 2024 10:08:40 -0400 Subject: [PATCH 339/428] quick fix --- pennylane_lightning/core/src/utils/UtilLinearAlg.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index 3e26787f9c..53f332628e 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -121,7 +121,7 @@ void compute_diagonalizing_gates(int n, int lda, std::filesystem::path scipyLibsPath = currentPath.parent_path().parent_path() / "scipy.libs"; if (!std::filesystem::exists(scipyLibsPath)) { - const std::string scipyPathStr(std::getenv(SCIPY_LIBS)); + const std::string scipyPathStr(std::getenv("SCIPY_LIBS")); PL_ABORT_IF(!std::filesystem::exists(scipyPathStr), "The SCIPY_LIBS env is not available."); scipyLibsPath = scipyPathStr; From cd3ebff4c155ece812c163825bec845d3f1c0d82 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Mon, 25 Mar 2024 10:23:20 -0400 Subject: [PATCH 340/428] debug windows --- .github/workflows/tests_windows.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/tests_windows.yml b/.github/workflows/tests_windows.yml index 4467b716b0..cfcf716214 100644 --- a/.github/workflows/tests_windows.yml +++ b/.github/workflows/tests_windows.yml @@ -210,6 +210,7 @@ jobs: - name: Build and run unit tests for code coverage run: | + set -x Subst Z: (pwd) Set-Location -Path "Z:\" python -c "import os; import site; os.environ['SCIPY_LIBS'] = f'{site.getsitepackages()[0]}/scipy.libs'" @@ -234,6 +235,7 @@ jobs: Invoke-Expression $cov_call } Move-Item -Path .\coverage.xml -Destination .\coverage-${{ github.job }}-${{ matrix.pl_backend }}.xml + set +x - name: Upload test results uses: actions/upload-artifact@v3 From 04fe74bf088d692e71444b10cf72caabb873d3ca Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Mon, 25 Mar 2024 10:26:59 -0400 Subject: [PATCH 341/428] quick test --- .github/workflows/tests_windows.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests_windows.yml b/.github/workflows/tests_windows.yml index cfcf716214..c3e9e75701 100644 --- a/.github/workflows/tests_windows.yml +++ b/.github/workflows/tests_windows.yml @@ -213,7 +213,7 @@ jobs: set -x Subst Z: (pwd) Set-Location -Path "Z:\" - python -c "import os; import site; os.environ['SCIPY_LIBS'] = f'{site.getsitepackages()[0]}/scipy.libs'" + python -c "import os; import site; os.environ['SCIPY_LIBS'] = f'{site.getsitepackages()[0]}/Lib/scipy.libs'" cmake -BBuild ` -DBUILD_TESTS=ON ` -DENABLE_PYTHON=OFF ` From 241053470a82763594f6ced8715e19d956349269 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Mon, 25 Mar 2024 10:48:16 -0400 Subject: [PATCH 342/428] debug getenv --- pennylane_lightning/core/src/utils/UtilLinearAlg.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index 53f332628e..8fbb32b30d 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -116,11 +116,12 @@ void compute_diagonalizing_gates(int n, int lda, #else std::shared_ptr blasLib; std::vector> blasLibs; - + PL_ABORT_IF(getPath()==nullptr, "Can't get path to lightning lib so."); std::filesystem::path currentPath(getPath()); std::filesystem::path scipyLibsPath = currentPath.parent_path().parent_path() / "scipy.libs"; if (!std::filesystem::exists(scipyLibsPath)) { + PL_ABORT_IF(std::getenv("SCIPY_LIBS")==nullptr, "Can't get SCIPY_LIBS env."); const std::string scipyPathStr(std::getenv("SCIPY_LIBS")); PL_ABORT_IF(!std::filesystem::exists(scipyPathStr), "The SCIPY_LIBS env is not available."); From 3bf3ff9550cbc7f4bcc105b9f272d5c364aee2f4 Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Mon, 25 Mar 2024 10:52:27 -0400 Subject: [PATCH 343/428] Updated PL branch in reqs-dev --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 2e5451f25a..3eb76c2ca1 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,5 +1,5 @@ pip~=22.0 -git+https://github.com/PennyLaneAI/pennylane.git@lightning-op-math +git+https://github.com/PennyLaneAI/pennylane.git@ham-tests ninja flaky pybind11 From 778ed5cc53134277ab21c549520ebb8f26d8775f Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Mon, 25 Mar 2024 11:05:16 -0400 Subject: [PATCH 344/428] debug python --- .github/workflows/tests_linux.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/tests_linux.yml b/.github/workflows/tests_linux.yml index 5f06095389..55c3f73ec4 100644 --- a/.github/workflows/tests_linux.yml +++ b/.github/workflows/tests_linux.yml @@ -115,6 +115,11 @@ jobs: runs-on: ${{ matrix.os }} steps: + - uses: actions/setup-python@v4 + name: Install Python + with: + python-version: '3.9' + - name: Checkout PennyLane-Lightning uses: actions/checkout@v3 with: @@ -137,11 +142,6 @@ jobs: git log -1 --format='%H' git status - - uses: actions/setup-python@v4 - name: Install Python - with: - python-version: '3.9' - - name: Install dependencies run: sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION @@ -181,6 +181,7 @@ jobs: - name: Install backend device run: | cd main + python -m pip install scipy CMAKE_ARGS="-DPL_BACKEND=${{ matrix.pl_backend }} -DLQ_ENABLE_KERNEL_OMP=ON -DENABLE_PYTHON=ON -DCMAKE_CXX_COMPILER=$(which g++-$GCC_VERSION)" \ python -m pip install -e . -vv From d6aa58ed5078ca3c143030806a1b7bd31f62abb4 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Mon, 25 Mar 2024 11:24:59 -0400 Subject: [PATCH 345/428] debug --- .github/workflows/tests_linux.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/tests_linux.yml b/.github/workflows/tests_linux.yml index 55c3f73ec4..50ffe8f682 100644 --- a/.github/workflows/tests_linux.yml +++ b/.github/workflows/tests_linux.yml @@ -343,6 +343,7 @@ jobs: - name: Install backend device run: | cd main + python -m pip install scipy CMAKE_ARGS="-DPL_BACKEND=${{ matrix.pl_backend }} -DENABLE_BLAS=ON -DENABLE_PYTHON=ON -DCMAKE_CXX_COMPILER=$(which g++-$GCC_VERSION)" \ python -m pip install -e . -vv @@ -544,6 +545,7 @@ jobs: if: ${{ matrix.pl_backend != 'all'}} run: | cd main + python -m pip install scipy SKIP_COMPILATION=True PL_BACKEND="lightning_qubit" pip install -e . -vv CMAKE_ARGS="-DPL_BACKEND=${{ matrix.pl_backend }} -DCMAKE_PREFIX_PATH=${{ github.workspace }}/Kokkos -DENABLE_PYTHON=ON -DCMAKE_CXX_COMPILER=$(which g++-$GCC_VERSION)" \ python -m pip install -e . -vv From d68ff615f62870c72a384ef04f563992e4f4089f Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Mon, 25 Mar 2024 11:26:34 -0400 Subject: [PATCH 346/428] Update requirements --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 3eb76c2ca1..9d63463cbe 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,5 +1,5 @@ pip~=22.0 -git+https://github.com/PennyLaneAI/pennylane.git@ham-tests +git+https://github.com/PennyLaneAI/pennylane.git@enable_new_opmath ninja flaky pybind11 From 190ebedf0f52f18a1fea2bcba9b05bdae82c087b Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Mon, 25 Mar 2024 11:37:41 -0400 Subject: [PATCH 347/428] fix workflow --- .github/workflows/tests_linux.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/tests_linux.yml b/.github/workflows/tests_linux.yml index 50ffe8f682..18fe5c81af 100644 --- a/.github/workflows/tests_linux.yml +++ b/.github/workflows/tests_linux.yml @@ -188,6 +188,7 @@ jobs: - name: Run PennyLane-Lightning unit tests run: | cd main/ + python -m pip install scipy DEVICENAME=`echo ${{ matrix.pl_backend }} | sed "s/_/./g"` OMP_NUM_THREADS=1 PL_DEVICE=${DEVICENAME} python -m pytest -n auto tests/ -k "not unitary_correct" $COVERAGE_FLAGS PL_DEVICE=${DEVICENAME} python -m pytest tests/ -k "unitary_correct" $COVERAGE_FLAGS --cov-append From 4f5ce80a1a6de5a7f649969262ceb1fa7843182f Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Mon, 25 Mar 2024 11:44:12 -0400 Subject: [PATCH 348/428] debug --- .github/workflows/tests_linux.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests_linux.yml b/.github/workflows/tests_linux.yml index 18fe5c81af..7a1afac34b 100644 --- a/.github/workflows/tests_linux.yml +++ b/.github/workflows/tests_linux.yml @@ -188,13 +188,14 @@ jobs: - name: Run PennyLane-Lightning unit tests run: | cd main/ - python -m pip install scipy + set -x DEVICENAME=`echo ${{ matrix.pl_backend }} | sed "s/_/./g"` OMP_NUM_THREADS=1 PL_DEVICE=${DEVICENAME} python -m pytest -n auto tests/ -k "not unitary_correct" $COVERAGE_FLAGS PL_DEVICE=${DEVICENAME} python -m pytest tests/ -k "unitary_correct" $COVERAGE_FLAGS --cov-append pl-device-test --device ${DEVICENAME} --skip-ops --shots=20000 $COVERAGE_FLAGS --cov-append pl-device-test --device ${DEVICENAME} --shots=None --skip-ops $COVERAGE_FLAGS --cov-append mv .coverage .coverage-${{ github.job }}-${{ matrix.pl_backend }} + set +x - name: Upload code coverage results uses: actions/upload-artifact@v3 From 78863858de492e044fde234c0026b6a49edc5098 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Mon, 25 Mar 2024 11:55:24 -0400 Subject: [PATCH 349/428] debug --- pennylane_lightning/core/src/utils/UtilLinearAlg.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index 8fbb32b30d..18c6cd9c78 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -117,10 +117,12 @@ void compute_diagonalizing_gates(int n, int lda, std::shared_ptr blasLib; std::vector> blasLibs; PL_ABORT_IF(getPath()==nullptr, "Can't get path to lightning lib so."); + std::string currentPathStr(getPath()); std::filesystem::path currentPath(getPath()); std::filesystem::path scipyLibsPath = currentPath.parent_path().parent_path() / "scipy.libs"; if (!std::filesystem::exists(scipyLibsPath)) { + PL_ABORT_IF_NOT(currentPathStr.find("pennylane_lightning")!=std::string::npos, "current path is not expected"); PL_ABORT_IF(std::getenv("SCIPY_LIBS")==nullptr, "Can't get SCIPY_LIBS env."); const std::string scipyPathStr(std::getenv("SCIPY_LIBS")); PL_ABORT_IF(!std::filesystem::exists(scipyPathStr), From 295d9a2051e5b5cee122140f0c6716d82fa8fb4d Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Mon, 25 Mar 2024 13:56:26 -0400 Subject: [PATCH 350/428] use absoute path for file exists check --- pennylane_lightning/core/src/utils/UtilLinearAlg.hpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index 18c6cd9c78..745d51ac39 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -116,14 +116,16 @@ void compute_diagonalizing_gates(int n, int lda, #else std::shared_ptr blasLib; std::vector> blasLibs; - PL_ABORT_IF(getPath()==nullptr, "Can't get path to lightning lib so."); + PL_ABORT_IF(getPath() == nullptr, "Can't get path to lightning lib so."); std::string currentPathStr(getPath()); std::filesystem::path currentPath(getPath()); std::filesystem::path scipyLibsPath = currentPath.parent_path().parent_path() / "scipy.libs"; - if (!std::filesystem::exists(scipyLibsPath)) { - PL_ABORT_IF_NOT(currentPathStr.find("pennylane_lightning")!=std::string::npos, "current path is not expected"); - PL_ABORT_IF(std::getenv("SCIPY_LIBS")==nullptr, "Can't get SCIPY_LIBS env."); + if (!std::filesystem::exists(std::filesystem::canonical(scipyLibsPath))) { + // PL_ABORT_IF_NOT(currentPathStr.find("pennylane_lightning")!=std::string::npos, + // "current path is not expected"); + PL_ABORT_IF(std::getenv("SCIPY_LIBS") == nullptr, + "Can't get SCIPY_LIBS env."); const std::string scipyPathStr(std::getenv("SCIPY_LIBS")); PL_ABORT_IF(!std::filesystem::exists(scipyPathStr), "The SCIPY_LIBS env is not available."); From 464c5d60417ba0faaab7ef19a629daa1b9bc1860 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Mon, 25 Mar 2024 14:14:31 -0400 Subject: [PATCH 351/428] link against dl --- pennylane_lightning/core/src/utils/CMakeLists.txt | 2 +- pennylane_lightning/core/src/utils/UtilLinearAlg.hpp | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/pennylane_lightning/core/src/utils/CMakeLists.txt b/pennylane_lightning/core/src/utils/CMakeLists.txt index a8a8b7ef5c..8e78376895 100644 --- a/pennylane_lightning/core/src/utils/CMakeLists.txt +++ b/pennylane_lightning/core/src/utils/CMakeLists.txt @@ -14,7 +14,7 @@ endif() target_include_directories(lightning_utils INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(lightning_utils INTERFACE lightning_compile_options lightning_external_libs - ) + dl) set_property(TARGET lightning_utils PROPERTY POSITION_INDEPENDENT_CODE ON) if (BUILD_TESTS) diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index 745d51ac39..218ae05625 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -28,6 +28,8 @@ #include #include +#include + #if defined(__APPLE__) || defined(__linux__) #include #elif defined(_MSC_VER) @@ -60,8 +62,8 @@ std::unordered_map priority_lib = { const char *getPath() { Dl_info dl_info; - PL_ABORT_IF(dladdr((const void *)getPath, &dl_info) == 0, - "Can't get the path to the shared library."); + auto flag = dladdr((const void *)getPath, &dl_info); + PL_ABORT_IF(!flag, "Can't get the path to the shared library."); return dl_info.dli_fname; } #elif defined(_MSC_VER) @@ -121,6 +123,9 @@ void compute_diagonalizing_gates(int n, int lda, std::filesystem::path currentPath(getPath()); std::filesystem::path scipyLibsPath = currentPath.parent_path().parent_path() / "scipy.libs"; + + std::cout<< scipyLibsPath < Date: Mon, 25 Mar 2024 14:49:29 -0400 Subject: [PATCH 352/428] test --- pennylane_lightning/core/src/utils/UtilLinearAlg.hpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index 218ae05625..b05a1a9a96 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -60,9 +60,11 @@ std::unordered_map priority_lib = { return path; }*/ +void libA_boundary() {} + const char *getPath() { Dl_info dl_info; - auto flag = dladdr((const void *)getPath, &dl_info); + auto flag = dladdr((const void *)libA_boundary, &dl_info); PL_ABORT_IF(!flag, "Can't get the path to the shared library."); return dl_info.dli_fname; } From 42a370c105f9faeb136fbd10727eb4a59a0e4b18 Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Mon, 25 Mar 2024 15:54:34 -0400 Subject: [PATCH 353/428] Added fix for prod with overlapping wires --- pennylane_lightning/core/_serialize.py | 11 ++++++++--- tests/test_serialize.py | 20 ++++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/pennylane_lightning/core/_serialize.py b/pennylane_lightning/core/_serialize.py index ad2abb5598..e033da1970 100644 --- a/pennylane_lightning/core/_serialize.py +++ b/pennylane_lightning/core/_serialize.py @@ -271,7 +271,9 @@ def _ob(self, observable, wires_map: dict = None): """Serialize a :class:`pennylane.operation.Observable` into an Observable.""" if isinstance(observable, (Prod, Sum, SProd)) and observable.pauli_rep is not None: return self._pauli_sentence(observable.pauli_rep, wires_map) - if isinstance(observable, (Tensor, Prod)): + if isinstance(observable, Tensor) or ( + isinstance(observable, Prod) and not observable.has_overlapping_wires + ): return self._tensor_ob(observable, wires_map) if observable.name in ("Hamiltonian", "LinearCombination"): return self._hamiltonian(observable, wires_map) @@ -294,7 +296,8 @@ def serialize_observables(self, tape: QuantumTape, wires_map: dict = None) -> Li Returns: list(ObsStructC128 or ObsStructC64): A list of observable objects compatible with - the C++ backend + the C++ backend. For unsupported observables, the observable matrix is used + to create a :class:`~pennylane.Hermitian` to be used for serialization. """ serialized_obs = [] @@ -310,7 +313,9 @@ def serialize_observables(self, tape: QuantumTape, wires_map: dict = None) -> Li offset_indices.append(offset_indices[-1] + 1) return serialized_obs, offset_indices - def serialize_ops(self, tape: QuantumTape, wires_map: dict = None) -> Tuple[ + def serialize_ops( + self, tape: QuantumTape, wires_map: dict = None + ) -> Tuple[ List[List[str]], List[np.ndarray], List[List[int]], diff --git a/tests/test_serialize.py b/tests/test_serialize.py index 9accbe9739..efedf4969f 100644 --- a/tests/test_serialize.py +++ b/tests/test_serialize.py @@ -150,6 +150,26 @@ def test_tensor_non_tensor_return(self, use_csingle, wires_map): ) assert s == s_expected + @pytest.mark.parametrize("use_csingle", [True, False]) + @pytest.mark.parametrize("wires_map", [wires_dict, None]) + def test_prod_return_with_overlapping_wires(self, use_csingle, wires_map): + """Test the expected serialization for a Prod return with operands with overlapping wires.""" + obs = qml.prod( + qml.sum(qml.X(0), qml.s_prod(2, qml.Hadamard(0))), + qml.sum(qml.s_prod(3, qml.Z(1)), qml.Z(2), qml.Hermitian(np.eye(2), wires=0)), + ) + tape = qml.tape.QuantumScript([], [qml.expval(obs)]) + + hermitian_obs = HermitianObsC64 if use_csingle else HermitianObsC128 + c_dtype = np.complex64 if use_csingle else np.complex128 + mat = obs.matrix().ravel().astype(c_dtype) + + s, _ = QuantumScriptSerializer(device_name, use_csingle).serialize_observables( + tape, wires_map + ) + s_expected = hermitian_obs(mat, [0, 1, 2]) + assert s[0] == s_expected + @pytest.mark.parametrize("use_csingle", [True, False]) @pytest.mark.parametrize("wires_map", [wires_dict, None]) def test_hermitian_return(self, use_csingle, wires_map): From 2ca991deaad52179b83d7775a0eaf11bd03ddd7c Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Mon, 25 Mar 2024 16:03:30 -0400 Subject: [PATCH 354/428] Formatting --- pennylane_lightning/core/_serialize.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pennylane_lightning/core/_serialize.py b/pennylane_lightning/core/_serialize.py index e033da1970..ae64574b61 100644 --- a/pennylane_lightning/core/_serialize.py +++ b/pennylane_lightning/core/_serialize.py @@ -313,9 +313,7 @@ def serialize_observables(self, tape: QuantumTape, wires_map: dict = None) -> Li offset_indices.append(offset_indices[-1] + 1) return serialized_obs, offset_indices - def serialize_ops( - self, tape: QuantumTape, wires_map: dict = None - ) -> Tuple[ + def serialize_ops(self, tape: QuantumTape, wires_map: dict = None) -> Tuple[ List[List[str]], List[np.ndarray], List[List[int]], From f3746d628a33a79ae71cce568a2a525688f8d21d Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Mon, 25 Mar 2024 16:40:03 -0400 Subject: [PATCH 355/428] test with -rdynamic --- pennylane_lightning/core/src/utils/CMakeLists.txt | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/pennylane_lightning/core/src/utils/CMakeLists.txt b/pennylane_lightning/core/src/utils/CMakeLists.txt index 8e78376895..fee25ffd2f 100644 --- a/pennylane_lightning/core/src/utils/CMakeLists.txt +++ b/pennylane_lightning/core/src/utils/CMakeLists.txt @@ -1,5 +1,7 @@ cmake_minimum_required(VERSION 3.20) +set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -rdynamic") + project(lightning_utils LANGUAGES CXX) set(LQUBIT_UTILS_FILES RuntimeInfo.cpp UtilLinearAlg.cpp CACHE INTERNAL "" FORCE) @@ -7,14 +9,11 @@ set(LQUBIT_UTILS_FILES RuntimeInfo.cpp UtilLinearAlg.cpp CACHE INTERNAL "" FORCE add_library(lightning_utils STATIC ${LQUBIT_UTILS_FILES}) -if(ENABLE_PYTHON) - target_compile_options(lightning_compile_options INTERFACE "-D_ENABLE_PYTHON=1") -endif() - target_include_directories(lightning_utils INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(lightning_utils INTERFACE lightning_compile_options - lightning_external_libs - dl) + lightning_external_libs) + + set_property(TARGET lightning_utils PROPERTY POSITION_INDEPENDENT_CODE ON) if (BUILD_TESTS) From de9e25d2b3f18900f416a37627ca1ca956e53c95 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Mon, 25 Mar 2024 16:52:13 -0400 Subject: [PATCH 356/428] link against dl --- pennylane_lightning/core/src/utils/CMakeLists.txt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pennylane_lightning/core/src/utils/CMakeLists.txt b/pennylane_lightning/core/src/utils/CMakeLists.txt index fee25ffd2f..2027c44d03 100644 --- a/pennylane_lightning/core/src/utils/CMakeLists.txt +++ b/pennylane_lightning/core/src/utils/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.20) -set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -rdynamic") - project(lightning_utils LANGUAGES CXX) set(LQUBIT_UTILS_FILES RuntimeInfo.cpp UtilLinearAlg.cpp CACHE INTERNAL "" FORCE) @@ -9,9 +7,14 @@ set(LQUBIT_UTILS_FILES RuntimeInfo.cpp UtilLinearAlg.cpp CACHE INTERNAL "" FORCE add_library(lightning_utils STATIC ${LQUBIT_UTILS_FILES}) + +set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -rdynamic") + + target_include_directories(lightning_utils INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(lightning_utils INTERFACE lightning_compile_options - lightning_external_libs) + lightning_external_libs + dl) set_property(TARGET lightning_utils PROPERTY POSITION_INDEPENDENT_CODE ON) From d0d75cd30d0f0dc5301fb8c1253fc710a0500162 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Mon, 25 Mar 2024 23:22:38 -0400 Subject: [PATCH 357/428] revert to get scipylibs with config.h.in --- CMakeLists.txt | 19 ++++++- .../core/src/utils/CMakeLists.txt | 5 +- .../core/src/utils/UtilLinearAlg.hpp | 54 ++----------------- .../core/src/utils/config.h.in | 4 ++ 4 files changed, 27 insertions(+), 55 deletions(-) create mode 100644 pennylane_lightning/core/src/utils/config.h.in diff --git a/CMakeLists.txt b/CMakeLists.txt index 523a65da2f..330a092496 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -81,8 +81,25 @@ set(CMAKE_POLICY_DEFAULT_CMP0127 NEW) # To suppress pybind11 CMP0127 warning # Add pybind11 include(FetchContent) +find_package(Python COMPONENTS Interpreter Development) + +message("Python site-packages directory: ${Python_SITELIB}") + +if (CMAKE_SYSTEM_NAME MATCHES "Windows" OR CMAKE_SYSTEM_NAME MATCHES "Linux") + set(SCIPYLIBS ${Python_SITELIB}/scipy.libs) + + if(NOT EXISTS ${SCIPYLIBS}) + message(FATAL_ERROR, "scipy.libs is not found.") + else() + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h) + endif() +else() + set(SCIPYLIBS "/System/Library/Frameworks/Accelerate.framework/Versions/Current/Frameworks/vecLib.framework/libLAPACK.dylib") + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h) +endif() + + if(ENABLE_PYTHON) - find_package(Python COMPONENTS Interpreter Development) FetchContent_Declare(pybind11 GIT_REPOSITORY https://github.com/pybind/pybind11.git GIT_TAG v2.11.1 diff --git a/pennylane_lightning/core/src/utils/CMakeLists.txt b/pennylane_lightning/core/src/utils/CMakeLists.txt index 2027c44d03..18261d48fd 100644 --- a/pennylane_lightning/core/src/utils/CMakeLists.txt +++ b/pennylane_lightning/core/src/utils/CMakeLists.txt @@ -8,13 +8,10 @@ set(LQUBIT_UTILS_FILES RuntimeInfo.cpp UtilLinearAlg.cpp CACHE INTERNAL "" FORCE add_library(lightning_utils STATIC ${LQUBIT_UTILS_FILES}) -set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -rdynamic") - target_include_directories(lightning_utils INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(lightning_utils INTERFACE lightning_compile_options - lightning_external_libs - dl) + lightning_external_libs) set_property(TARGET lightning_utils PROPERTY POSITION_INDEPENDENT_CODE ON) diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index b05a1a9a96..753d509682 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -28,8 +28,6 @@ #include #include -#include - #if defined(__APPLE__) || defined(__linux__) #include #elif defined(_MSC_VER) @@ -37,6 +35,7 @@ #endif #include "SharedLibLoader.hpp" +#include "config.h" /// @cond DEV namespace { @@ -50,33 +49,6 @@ using cheevPtr = void (*)(const char *, const char *, const int *, std::unordered_map priority_lib = { {"stdc", 0}, {"gcc", 1}, {"quadmath", 2}, {"gfortran", 3}, {"openblas", 4}}; - -#ifdef __linux__ -/*std::string getPath() { - Dl_info dl_info; - auto flag = dladdr((const void *)getPath, &dl_info); - PL_ABORT_IF(!flag, "Can't get the path to the shared library."); - std::string path(dl_info.dli_fname); - return path; -}*/ - -void libA_boundary() {} - -const char *getPath() { - Dl_info dl_info; - auto flag = dladdr((const void *)libA_boundary, &dl_info); - PL_ABORT_IF(!flag, "Can't get the path to the shared library."); - return dl_info.dli_fname; -} -#elif defined(_MSC_VER) -std::string getPath() { - char buffer[MAX_PATH]; - GetModuleFileName(nullptr, buffer, MAX_PATH); - std::string fullPath(buffer); - std::size_t pos = fullPath.find_last_of("\\/"); - return fullPath.substr(0, pos); -} -#endif } // namespace /// @endcond @@ -112,32 +84,14 @@ void compute_diagonalizing_gates(int n, int lda, } } #ifdef __APPLE__ - const std::string libName = - "/System/Library/Frameworks/Accelerate.framework/Versions/Current/" - "Frameworks/vecLib.framework/libLAPACK.dylib"; + const std::string libName(SCIPY_LIBS_PATH); std::shared_ptr blasLib = std::make_shared(libName); #else std::shared_ptr blasLib; std::vector> blasLibs; - PL_ABORT_IF(getPath() == nullptr, "Can't get path to lightning lib so."); - std::string currentPathStr(getPath()); - std::filesystem::path currentPath(getPath()); - std::filesystem::path scipyLibsPath = - currentPath.parent_path().parent_path() / "scipy.libs"; - - std::cout<< scipyLibsPath <> availableLibs; diff --git a/pennylane_lightning/core/src/utils/config.h.in b/pennylane_lightning/core/src/utils/config.h.in new file mode 100644 index 0000000000..d8a0fd1a7f --- /dev/null +++ b/pennylane_lightning/core/src/utils/config.h.in @@ -0,0 +1,4 @@ +#ifndef CONFIG_H +#define CONFIG_H +#define SCIPY_LIBS_PATH "${SCIPYLIBS}" +#endif \ No newline at end of file From caf83ef8820bfce0bb7f668201664918e147177b Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Mon, 25 Mar 2024 23:36:10 -0400 Subject: [PATCH 358/428] windows fix --- .github/workflows/tests_windows.yml | 2 -- CMakeLists.txt | 7 +++++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests_windows.yml b/.github/workflows/tests_windows.yml index c3e9e75701..90e2ff6a2d 100644 --- a/.github/workflows/tests_windows.yml +++ b/.github/workflows/tests_windows.yml @@ -210,7 +210,6 @@ jobs: - name: Build and run unit tests for code coverage run: | - set -x Subst Z: (pwd) Set-Location -Path "Z:\" python -c "import os; import site; os.environ['SCIPY_LIBS'] = f'{site.getsitepackages()[0]}/Lib/scipy.libs'" @@ -235,7 +234,6 @@ jobs: Invoke-Expression $cov_call } Move-Item -Path .\coverage.xml -Destination .\coverage-${{ github.job }}-${{ matrix.pl_backend }}.xml - set +x - name: Upload test results uses: actions/upload-artifact@v3 diff --git a/CMakeLists.txt b/CMakeLists.txt index 330a092496..e425bbc62d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -88,6 +88,13 @@ message("Python site-packages directory: ${Python_SITELIB}") if (CMAKE_SYSTEM_NAME MATCHES "Windows" OR CMAKE_SYSTEM_NAME MATCHES "Linux") set(SCIPYLIBS ${Python_SITELIB}/scipy.libs) + message("Python site-packages directory (Windows): ${SCIPYLIBS}") + + if (CMAKE_SYSTEM_NAME MATCHES "Windows") + string(REPLACE "\\" "/" SCIPYLIBS "${SCIPYLIBS}") + message("Python site-packages directory (Windows): ${SCIPYLIBS}") + endif() + if(NOT EXISTS ${SCIPYLIBS}) message(FATAL_ERROR, "scipy.libs is not found.") else() From a2dd9ca745c74b553a2811292dd5bf1cfc069fb0 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Tue, 26 Mar 2024 00:02:08 -0400 Subject: [PATCH 359/428] setup win wheels build venv with CIBW_BEFORE_ALL --- .github/workflows/tests_windows.yml | 2 -- .github/workflows/wheel_win_x86_64.yml | 7 ++++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests_windows.yml b/.github/workflows/tests_windows.yml index 90e2ff6a2d..66bcce17ed 100644 --- a/.github/workflows/tests_windows.yml +++ b/.github/workflows/tests_windows.yml @@ -122,7 +122,6 @@ jobs: - name: Build and run unit tests for code coverage run: | - python -c "import os; import site; os.environ['SCIPY_LIBS'] = f'{site.getsitepackages()[0]}/scipy.libs'" cmake -BBuild ` -DBUILD_TESTS=ON ` -DENABLE_OPENMP=OFF ` @@ -212,7 +211,6 @@ jobs: run: | Subst Z: (pwd) Set-Location -Path "Z:\" - python -c "import os; import site; os.environ['SCIPY_LIBS'] = f'{site.getsitepackages()[0]}/Lib/scipy.libs'" cmake -BBuild ` -DBUILD_TESTS=ON ` -DENABLE_PYTHON=OFF ` diff --git a/.github/workflows/wheel_win_x86_64.yml b/.github/workflows/wheel_win_x86_64.yml index bb4292a3e7..0b1313186a 100644 --- a/.github/workflows/wheel_win_x86_64.yml +++ b/.github/workflows/wheel_win_x86_64.yml @@ -124,13 +124,18 @@ jobs: - name: Build wheels env: + CIBW_BEFORE_ALL: | + python -m venv venv + source venv/bin/activate + python -m pip install scipy + CIBW_ARCHS_WINDOWS: ${{matrix.arch}} CIBW_BUILD: ${{ matrix.cibw_build }} # Python build settings CIBW_BEFORE_BUILD: | - python -m pip install pybind11 cmake~=3.24.0 build scipy + python -m pip install pybind11 cmake~=3.24.0 build #Temporarily commenting while solving problems to find binaries in CIBW tests. # CIBW_BEFORE_TEST: | From 0b89302ed29a416f98e4eb8e1fd0f5035f8676da Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Tue, 26 Mar 2024 00:10:29 -0400 Subject: [PATCH 360/428] fix for windows wheel build --- .github/workflows/wheel_win_x86_64.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/wheel_win_x86_64.yml b/.github/workflows/wheel_win_x86_64.yml index 0b1313186a..1257942491 100644 --- a/.github/workflows/wheel_win_x86_64.yml +++ b/.github/workflows/wheel_win_x86_64.yml @@ -126,7 +126,7 @@ jobs: env: CIBW_BEFORE_ALL: | python -m venv venv - source venv/bin/activate + venv\Scripts\activate python -m pip install scipy CIBW_ARCHS_WINDOWS: ${{matrix.arch}} From ab73fb8d91e768bdaeba731e9a0f913591b6149b Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Tue, 26 Mar 2024 00:19:31 -0400 Subject: [PATCH 361/428] activate venv before windows wheels build --- .github/workflows/wheel_win_x86_64.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/wheel_win_x86_64.yml b/.github/workflows/wheel_win_x86_64.yml index 1257942491..b033aaabbf 100644 --- a/.github/workflows/wheel_win_x86_64.yml +++ b/.github/workflows/wheel_win_x86_64.yml @@ -127,7 +127,6 @@ jobs: CIBW_BEFORE_ALL: | python -m venv venv venv\Scripts\activate - python -m pip install scipy CIBW_ARCHS_WINDOWS: ${{matrix.arch}} @@ -150,7 +149,10 @@ jobs: CIBW_BUILD_FRONTEND: build - run: python -m cibuildwheel --output-dir wheelhouse + run: | + venv\Scripts\activate + python -m pip install scipy + python -m cibuildwheel --output-dir wheelhouse - name: Patch wheels run: | From fc581c6d145f14939cd98d4737be922acc47bc50 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Tue, 26 Mar 2024 00:23:50 -0400 Subject: [PATCH 362/428] quick fix --- .github/workflows/wheel_win_x86_64.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/wheel_win_x86_64.yml b/.github/workflows/wheel_win_x86_64.yml index b033aaabbf..6f539b3565 100644 --- a/.github/workflows/wheel_win_x86_64.yml +++ b/.github/workflows/wheel_win_x86_64.yml @@ -124,10 +124,6 @@ jobs: - name: Build wheels env: - CIBW_BEFORE_ALL: | - python -m venv venv - venv\Scripts\activate - CIBW_ARCHS_WINDOWS: ${{matrix.arch}} CIBW_BUILD: ${{ matrix.cibw_build }} @@ -150,6 +146,7 @@ jobs: CIBW_BUILD_FRONTEND: build run: | + python -m venv venv venv\Scripts\activate python -m pip install scipy python -m cibuildwheel --output-dir wheelhouse From 77b3b7ba686fecd6bc92bf40fbfeaffb6919f671 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Tue, 26 Mar 2024 00:28:39 -0400 Subject: [PATCH 363/428] quick fix win wheels --- .github/workflows/wheel_win_x86_64.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/wheel_win_x86_64.yml b/.github/workflows/wheel_win_x86_64.yml index 6f539b3565..57007985ff 100644 --- a/.github/workflows/wheel_win_x86_64.yml +++ b/.github/workflows/wheel_win_x86_64.yml @@ -148,7 +148,7 @@ jobs: run: | python -m venv venv venv\Scripts\activate - python -m pip install scipy + python -m pip install scipy cibuildwheel~=2.16.0 wheel python -m cibuildwheel --output-dir wheelhouse - name: Patch wheels From 9f2b7940f5ea71de45b675dcd423415d0cc01f54 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Tue, 26 Mar 2024 00:29:24 -0400 Subject: [PATCH 364/428] quick fix for win wheel build --- .github/workflows/wheel_win_x86_64.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/wheel_win_x86_64.yml b/.github/workflows/wheel_win_x86_64.yml index 57007985ff..7226ae3afe 100644 --- a/.github/workflows/wheel_win_x86_64.yml +++ b/.github/workflows/wheel_win_x86_64.yml @@ -83,13 +83,13 @@ jobs: cmake --install ./Build --config RelWithDebInfo --verbose win-wheels: - needs: [set_wheel_build_matrix, build_dependencies] + needs: [set_wheel_build_matrix] strategy: fail-fast: false matrix: os: [windows-2022] arch: [AMD64] - pl_backend: ["lightning_kokkos", "lightning_qubit"] + pl_backend: ["lightning_qubit"] cibw_build: ${{ fromJson(needs.set_wheel_build_matrix.outputs.python_version) }} exec_model: [\"SERIAL\"] kokkos_version: ${{ fromJson(needs.set_wheel_build_matrix.outputs.kokkos_version) }} From 25e30b86e55a4140440f9230c74bb9e4e1da5b86 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Tue, 26 Mar 2024 00:50:45 -0400 Subject: [PATCH 365/428] add scipy to requirements --- .github/workflows/wheel_win_x86_64.yml | 5 +---- setup.py | 1 + 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/wheel_win_x86_64.yml b/.github/workflows/wheel_win_x86_64.yml index 7226ae3afe..148ee098e7 100644 --- a/.github/workflows/wheel_win_x86_64.yml +++ b/.github/workflows/wheel_win_x86_64.yml @@ -130,7 +130,7 @@ jobs: # Python build settings CIBW_BEFORE_BUILD: | - python -m pip install pybind11 cmake~=3.24.0 build + python -m pip install pybind11 cmake~=3.24.0 build scipy #Temporarily commenting while solving problems to find binaries in CIBW tests. # CIBW_BEFORE_TEST: | @@ -146,9 +146,6 @@ jobs: CIBW_BUILD_FRONTEND: build run: | - python -m venv venv - venv\Scripts\activate - python -m pip install scipy cibuildwheel~=2.16.0 wheel python -m cibuildwheel --output-dir wheelhouse - name: Patch wheels diff --git a/setup.py b/setup.py index 977f753559..21b2142240 100644 --- a/setup.py +++ b/setup.py @@ -171,6 +171,7 @@ def build_extension(self, ext: CMakeExtension): requirements = [ "pennylane>=0.34", + "scipy" ] packages_list = ["pennylane_lightning." + backend] From b2f4e8d616410f098d1b14a7b80d087966545eb4 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Tue, 26 Mar 2024 10:05:53 -0400 Subject: [PATCH 366/428] runtime load in relative path fix test --- .../core/src/utils/UtilLinearAlg.hpp | 31 +++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index 753d509682..86a61cae5f 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -28,6 +28,8 @@ #include #include +#include + #if defined(__APPLE__) || defined(__linux__) #include #elif defined(_MSC_VER) @@ -35,7 +37,7 @@ #endif #include "SharedLibLoader.hpp" -#include "config.h" +//#include "config.h" /// @cond DEV namespace { @@ -54,6 +56,23 @@ std::unordered_map priority_lib = { namespace Pennylane::Util { +#ifdef __linux__ +const char *getPath() { + Dl_info dl_info; + PL_ABORT_IF(dladdr((const void *)getPath, &dl_info) == 0, + "Can't get the path to the shared library."); + return dl_info.dli_fname; +} +#elif defined(_MSC_VER) +std::string getPath() { + char buffer[MAX_PATH]; + GetModuleFileName(nullptr, buffer, MAX_PATH); + std::string fullPath(buffer); + std::size_t pos = fullPath.find_last_of("\\/"); + return fullPath.substr(0, pos); +} +#endif + /** * @brief Decompose Hermitian matrix into diagonal matrix and unitaries * @@ -91,7 +110,15 @@ void compute_diagonalizing_gates(int n, int lda, std::shared_ptr blasLib; std::vector> blasLibs; - std::filesystem::path scipyLibsPath(SCIPY_LIBS_PATH); + //std::filesystem::path scipyLibsPath(SCIPY_LIBS_PATH); + + std::string currentPathStr(getPath()); + std::string scipyPath = currentPathStr + "/../../scipy.libs"; + + std::filesystem::path scipyLibsPath(scipyPath); + + std::cout<> availableLibs; From dc79e6e2d93f7a41fa706147b3de1cb6e421de42 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Tue, 26 Mar 2024 10:07:12 -0400 Subject: [PATCH 367/428] remove scipy from requirements in setup.py --- setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.py b/setup.py index 21b2142240..977f753559 100644 --- a/setup.py +++ b/setup.py @@ -171,7 +171,6 @@ def build_extension(self, ext: CMakeExtension): requirements = [ "pennylane>=0.34", - "scipy" ] packages_list = ["pennylane_lightning." + backend] From 2e5179c8ed616c06cc7e04cca649c6633a5d1415 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Tue, 26 Mar 2024 10:14:45 -0400 Subject: [PATCH 368/428] add inline to fix compile time --- pennylane_lightning/core/src/utils/UtilLinearAlg.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index 86a61cae5f..cff5ce3c0f 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -57,14 +57,14 @@ std::unordered_map priority_lib = { namespace Pennylane::Util { #ifdef __linux__ -const char *getPath() { +inline const char *getPath() { Dl_info dl_info; PL_ABORT_IF(dladdr((const void *)getPath, &dl_info) == 0, "Can't get the path to the shared library."); return dl_info.dli_fname; } #elif defined(_MSC_VER) -std::string getPath() { +inline std::string getPath() { char buffer[MAX_PATH]; GetModuleFileName(nullptr, buffer, MAX_PATH); std::string fullPath(buffer); From 0edb84279db5aa2d507b7bd24d4d5104ca85bc6d Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Tue, 26 Mar 2024 10:29:43 -0400 Subject: [PATCH 369/428] Point requirements to PL master --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 9d63463cbe..02aa0c47f9 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,5 +1,5 @@ pip~=22.0 -git+https://github.com/PennyLaneAI/pennylane.git@enable_new_opmath +git+https://github.com/PennyLaneAI/pennylane.git@master ninja flaky pybind11 From 3dadb696d22761c553b135f9e3366d29dad9266d Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Tue, 26 Mar 2024 10:37:55 -0400 Subject: [PATCH 370/428] support both rpath and SCIPY_LIBS_PATH search --- .../core/src/utils/CMakeLists.txt | 4 ++- .../core/src/utils/UtilLinearAlg.hpp | 25 +++++++++++++------ 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/pennylane_lightning/core/src/utils/CMakeLists.txt b/pennylane_lightning/core/src/utils/CMakeLists.txt index 18261d48fd..9b0775c946 100644 --- a/pennylane_lightning/core/src/utils/CMakeLists.txt +++ b/pennylane_lightning/core/src/utils/CMakeLists.txt @@ -7,7 +7,9 @@ set(LQUBIT_UTILS_FILES RuntimeInfo.cpp UtilLinearAlg.cpp CACHE INTERNAL "" FORCE add_library(lightning_utils STATIC ${LQUBIT_UTILS_FILES}) - +if(ENABLE_PYTHON) + target_link_libraries(lightning_compile_options INTERFACE "-D_ENABLE_PYTHON=1") +endif() target_include_directories(lightning_utils INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(lightning_utils INTERFACE lightning_compile_options diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index cff5ce3c0f..3cd7cb29c8 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -37,7 +37,13 @@ #endif #include "SharedLibLoader.hpp" -//#include "config.h" +#ifndef _ENABLE_PYTHON +#include "config.h" +#endif + +#ifdef __APPLE__ +#include "config.h" +#endif /// @cond DEV namespace { @@ -110,15 +116,20 @@ void compute_diagonalizing_gates(int n, int lda, std::shared_ptr blasLib; std::vector> blasLibs; - //std::filesystem::path scipyLibsPath(SCIPY_LIBS_PATH); +#ifndef _ENABLE_PYTHON + std::string scipyPathStr(SCIPY_LIBS_PATH); +#else + std::string scipyPathStr; +#endif - std::string currentPathStr(getPath()); - std::string scipyPath = currentPathStr + "/../../scipy.libs"; + if (scipyPathStr.empty()) { + std::string currentPathStr(getPath()); + scipyPathStr = currentPathStr + "/../../scipy.libs"; + } - std::filesystem::path scipyLibsPath(scipyPath); - - std::cout<> availableLibs; From feca13c9fb9686b8cd42790aed761c6e11ae82ca Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Tue, 26 Mar 2024 11:14:32 -0400 Subject: [PATCH 371/428] Pinning cmake --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 02aa0c47f9..474ef50d1f 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -14,6 +14,6 @@ clang-tidy~=16.0 clang-format~=16.0 isort==5.13.2 click==8.0.4 -cmake +cmake==3.28.4 custatevec-cu12 pylint From 5ece74f61c0ef3cf217bf8ef36f75a77b61871d9 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Tue, 26 Mar 2024 11:17:51 -0400 Subject: [PATCH 372/428] add lapack.so search in default search path --- .github/workflows/tests_gpu_cuda.yml | 1 + .github/workflows/tests_gpu_kokkos.yml | 6 +- .github/workflows/tests_linux.yml | 12 ++-- .github/workflows/tests_linux_x86_mpi_gpu.yml | 1 + .../core/src/utils/SharedLibLoader.hpp | 4 +- .../core/src/utils/UtilLinearAlg.hpp | 64 +++++++++---------- pennylane_lightning/core/src/utils/config.h | 4 ++ 7 files changed, 50 insertions(+), 42 deletions(-) create mode 100644 pennylane_lightning/core/src/utils/config.h diff --git a/.github/workflows/tests_gpu_cuda.yml b/.github/workflows/tests_gpu_cuda.yml index cfe5d74e0c..b59d664d41 100644 --- a/.github/workflows/tests_gpu_cuda.yml +++ b/.github/workflows/tests_gpu_cuda.yml @@ -120,6 +120,7 @@ jobs: - name: Install required packages run: | python -m pip install ninja cmake scipy custatevec-cu${{ matrix.cuda_version }} + sudo apt-get -y -q install liblapack-dev - name: Build and run unit tests run: | diff --git a/.github/workflows/tests_gpu_kokkos.yml b/.github/workflows/tests_gpu_kokkos.yml index 7cfdf51148..19e51f51bf 100644 --- a/.github/workflows/tests_gpu_kokkos.yml +++ b/.github/workflows/tests_gpu_kokkos.yml @@ -80,7 +80,7 @@ jobs: CMAKE_PREFIX_PATH: ${{ github.workspace }}/Kokkos_install/${{ matrix.exec_model }} run: | set -x - sudo apt-get update && sudo apt-get -y -q install gfortran-$GCC_VERSION + sudo apt-get update && sudo apt-get -y -q install gfortran-$GCC_VERSION liblapack-dev cd kokkos_${{ matrix.exec_model }}/example/build_cmake_installed which -a gfortran || which -a gfortran-$GCC_VERSION cmake -B build -DCMAKE_Fortran_COMPILER=gfortran-$GCC_VERSION @@ -148,7 +148,7 @@ jobs: - name: Install dependencies run: | - sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION ninja-build gcovr lcov + sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION ninja-build gcovr lcov liblapack-dev python -m pip install scipy @@ -255,7 +255,7 @@ jobs: python-version: '3.9' - name: Install dependencies - run: sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION + run: sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION liblapack-dev - name: Get required Python packages run: | diff --git a/.github/workflows/tests_linux.yml b/.github/workflows/tests_linux.yml index 7a1afac34b..f99809eef6 100644 --- a/.github/workflows/tests_linux.yml +++ b/.github/workflows/tests_linux.yml @@ -50,7 +50,7 @@ jobs: uses: actions/checkout@v3 - name: Install dependencies - run: echo ${{ github.event_name }} && sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION ninja-build gcovr lcov + run: echo ${{ github.event_name }} && sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION ninja-build gcovr lcov liblapack-dev - name: Build and run unit tests run: | @@ -143,7 +143,7 @@ jobs: git status - name: Install dependencies - run: sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION + run: sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION liblapack-dev - name: Get required Python packages run: | @@ -228,7 +228,7 @@ jobs: run: python -m pip install scipy - name: Install dependencies - run: sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION libopenblas-dev ninja-build gcovr lcov + run: sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION libopenblas-dev ninja-build gcovr lcov liblapack-dev - name: Build and run unit tests run: | @@ -412,7 +412,7 @@ jobs: run: python -m pip install scipy - name: Install dependencies - run: sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION ninja-build gcovr lcov + run: sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION ninja-build gcovr lcov liblapack-dev - name: Build and run unit tests run: | @@ -508,7 +508,7 @@ jobs: pwd - name: Install dependencies - run: sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION + run: sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION liblapack-dev - name: Get required Python packages run: | @@ -679,7 +679,7 @@ jobs: - name: Install dependencies run: | - sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION ninja-build gcovr lcov + sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION ninja-build gcovr lcov liblapack-dev python -m pip install scipy - name: Build and run unit tests diff --git a/.github/workflows/tests_linux_x86_mpi_gpu.yml b/.github/workflows/tests_linux_x86_mpi_gpu.yml index 445993bad1..c1820365fe 100644 --- a/.github/workflows/tests_linux_x86_mpi_gpu.yml +++ b/.github/workflows/tests_linux_x86_mpi_gpu.yml @@ -95,6 +95,7 @@ jobs: run: | python -m pip install -r requirements-dev.txt python -m pip install cmake custatevec-cu12 + sudo apt-get -y -q install liblapack-dev - name: Validate GPU version and installed compiler and modules run: | diff --git a/pennylane_lightning/core/src/utils/SharedLibLoader.hpp b/pennylane_lightning/core/src/utils/SharedLibLoader.hpp index a846edf463..420879719c 100644 --- a/pennylane_lightning/core/src/utils/SharedLibLoader.hpp +++ b/pennylane_lightning/core/src/utils/SharedLibLoader.hpp @@ -55,7 +55,9 @@ class SharedLibLoader final { #if defined(__APPLE__) || defined(__linux__) handle_ = dlopen(filename.c_str(), rtld_flags); // This allows users to use pre-installed LAPACK package - PL_ABORT_IF(!handle_, dlerror()); + if(filename!="lapack.so"){ + PL_ABORT_IF(!handle_, dlerror()); + } #elif defined(_MSC_VER) // codefactor[skip] handle_ = LoadLibrary(filename.c_str()); diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index 3cd7cb29c8..0187e66776 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -37,13 +37,8 @@ #endif #include "SharedLibLoader.hpp" -#ifndef _ENABLE_PYTHON -#include "config.h" -#endif -#ifdef __APPLE__ #include "config.h" -#endif /// @cond DEV namespace { @@ -113,48 +108,53 @@ void compute_diagonalizing_gates(int n, int lda, std::shared_ptr blasLib = std::make_shared(libName); #else - std::shared_ptr blasLib; - std::vector> blasLibs; + std::shared_ptr blasLib("lapack.so"); + if(!blasLib->getHandle()){ + std::vector> blasLibs; -#ifndef _ENABLE_PYTHON - std::string scipyPathStr(SCIPY_LIBS_PATH); -#else - std::string scipyPathStr; -#endif + std::string scipyPathStr(SCIPY_LIBS_PATH); - if (scipyPathStr.empty()) { - std::string currentPathStr(getPath()); - scipyPathStr = currentPathStr + "/../../scipy.libs"; - } + if (scipyPathStr.empty() || !std::filesystem::exists(scipyPathStr)) { + std::string currentPathStr(getPath()); + scipyPathStr = currentPathStr + "/../../scipy.libs"; + if(!std::filesystem::exists(scipyPathStr)){ + + } + PL_ABORT_IF(!std::filesystem::exists(scipyPathStr), + "The scipy.libs/ is not available."); + } else { + std::exit(EXIT_FAILURE); + } - std::filesystem::path scipyLibsPath(scipyPathStr); + std::filesystem::path scipyLibsPath(scipyPathStr); - std::cout << scipyLibsPath << std::endl; + std::cout << scipyLibsPath << std::endl; - std::vector> availableLibs; + std::vector> availableLibs; - for (const auto &lib : std::filesystem::directory_iterator(scipyLibsPath)) { - if (lib.is_regular_file()) { - for (const auto &iter : priority_lib) { - std::string libname_str = lib.path().filename().string(); - if (libname_str.find(iter.first) != std::string ::npos) { - availableLibs.emplace_back(libname_str, iter.second); + for (const auto &lib : std::filesystem::directory_iterator(scipyLibsPath)) { + if (lib.is_regular_file()) { + for (const auto &iter : priority_lib) { + std::string libname_str = lib.path().filename().string(); + if (libname_str.find(iter.first) != std::string ::npos) { + availableLibs.emplace_back(libname_str, iter.second); + } } } } - } - std::sort(availableLibs.begin(), availableLibs.end(), + std::sort(availableLibs.begin(), availableLibs.end(), [](const auto &lhs, const auto &rhs) { return lhs.second < rhs.second; }); - for (const auto &lib : availableLibs) { - auto libPath = scipyLibsPath / lib.first.c_str(); - const std::string libPathStr = libPath.string(); - blasLibs.emplace_back(std::make_shared(libPathStr)); + for (const auto &lib : availableLibs) { + auto libPath = scipyLibsPath / lib.first.c_str(); + const std::string libPathStr = libPath.string(); + blasLibs.emplace_back(std::make_shared(libPathStr)); + } + blasLib = blasLibs.back(); } - blasLib = blasLibs.back(); #endif char jobz = 'V'; // Enable both eigenvalues and eigenvectors computation diff --git a/pennylane_lightning/core/src/utils/config.h b/pennylane_lightning/core/src/utils/config.h new file mode 100644 index 0000000000..877d4b943a --- /dev/null +++ b/pennylane_lightning/core/src/utils/config.h @@ -0,0 +1,4 @@ +#ifndef CONFIG_H +#define CONFIG_H +#define SCIPY_LIBS_PATH "" +#endif From 3f094d982b7914711d9bac0ef3c3b88f2ace4c3c Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Tue, 26 Mar 2024 11:18:14 -0400 Subject: [PATCH 373/428] make format --- .../core/src/utils/SharedLibLoader.hpp | 4 ++-- .../core/src/utils/UtilLinearAlg.hpp | 19 ++++++++++--------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/pennylane_lightning/core/src/utils/SharedLibLoader.hpp b/pennylane_lightning/core/src/utils/SharedLibLoader.hpp index 420879719c..4566901f50 100644 --- a/pennylane_lightning/core/src/utils/SharedLibLoader.hpp +++ b/pennylane_lightning/core/src/utils/SharedLibLoader.hpp @@ -55,8 +55,8 @@ class SharedLibLoader final { #if defined(__APPLE__) || defined(__linux__) handle_ = dlopen(filename.c_str(), rtld_flags); // This allows users to use pre-installed LAPACK package - if(filename!="lapack.so"){ - PL_ABORT_IF(!handle_, dlerror()); + if (filename != "lapack.so") { + PL_ABORT_IF(!handle_, dlerror()); } #elif defined(_MSC_VER) // codefactor[skip] diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index 0187e66776..598d9db212 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -109,7 +109,7 @@ void compute_diagonalizing_gates(int n, int lda, std::make_shared(libName); #else std::shared_ptr blasLib("lapack.so"); - if(!blasLib->getHandle()){ + if (!blasLib->getHandle()) { std::vector> blasLibs; std::string scipyPathStr(SCIPY_LIBS_PATH); @@ -117,11 +117,10 @@ void compute_diagonalizing_gates(int n, int lda, if (scipyPathStr.empty() || !std::filesystem::exists(scipyPathStr)) { std::string currentPathStr(getPath()); scipyPathStr = currentPathStr + "/../../scipy.libs"; - if(!std::filesystem::exists(scipyPathStr)){ - + if (!std::filesystem::exists(scipyPathStr)) { } PL_ABORT_IF(!std::filesystem::exists(scipyPathStr), - "The scipy.libs/ is not available."); + "The scipy.libs/ is not available."); } else { std::exit(EXIT_FAILURE); } @@ -132,7 +131,8 @@ void compute_diagonalizing_gates(int n, int lda, std::vector> availableLibs; - for (const auto &lib : std::filesystem::directory_iterator(scipyLibsPath)) { + for (const auto &lib : + std::filesystem::directory_iterator(scipyLibsPath)) { if (lib.is_regular_file()) { for (const auto &iter : priority_lib) { std::string libname_str = lib.path().filename().string(); @@ -144,14 +144,15 @@ void compute_diagonalizing_gates(int n, int lda, } std::sort(availableLibs.begin(), availableLibs.end(), - [](const auto &lhs, const auto &rhs) { - return lhs.second < rhs.second; - }); + [](const auto &lhs, const auto &rhs) { + return lhs.second < rhs.second; + }); for (const auto &lib : availableLibs) { auto libPath = scipyLibsPath / lib.first.c_str(); const std::string libPathStr = libPath.string(); - blasLibs.emplace_back(std::make_shared(libPathStr)); + blasLibs.emplace_back( + std::make_shared(libPathStr)); } blasLib = blasLibs.back(); } From 6b7d6c7ebbbfefef602b9e988b541e86411fafc7 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Tue, 26 Mar 2024 11:24:30 -0400 Subject: [PATCH 374/428] quick fix --- pennylane_lightning/core/src/utils/UtilLinearAlg.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index 598d9db212..a122bfc860 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -108,7 +108,8 @@ void compute_diagonalizing_gates(int n, int lda, std::shared_ptr blasLib = std::make_shared(libName); #else - std::shared_ptr blasLib("lapack.so"); + const std::string lapackName = "lapack.so"; + std::shared_ptr blasLib(lapackName); if (!blasLib->getHandle()) { std::vector> blasLibs; From 6eccfd2666e69f9c4495f437e3e8df3402fc5aee Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Tue, 26 Mar 2024 11:27:45 -0400 Subject: [PATCH 375/428] quick fix --- pennylane_lightning/core/src/utils/UtilLinearAlg.hpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index a122bfc860..05bb4f9df2 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -108,8 +108,7 @@ void compute_diagonalizing_gates(int n, int lda, std::shared_ptr blasLib = std::make_shared(libName); #else - const std::string lapackName = "lapack.so"; - std::shared_ptr blasLib(lapackName); + std::shared_ptr blasLib = std::make_shared("lapack.so"); if (!blasLib->getHandle()) { std::vector> blasLibs; From 1f2cd7ab7ceae7c70c2f425dd749db3935ab6a34 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Tue, 26 Mar 2024 11:36:31 -0400 Subject: [PATCH 376/428] install lapack --- .github/workflows/tests_gpu_cuda.yml | 3 +-- .github/workflows/tests_gpu_kokkos.yml | 5 +---- .github/workflows/tests_linux.yml | 17 ----------------- .github/workflows/tests_linux_x86_mpi_gpu.yml | 1 - 4 files changed, 2 insertions(+), 24 deletions(-) diff --git a/.github/workflows/tests_gpu_cuda.yml b/.github/workflows/tests_gpu_cuda.yml index b59d664d41..d45c34fe4d 100644 --- a/.github/workflows/tests_gpu_cuda.yml +++ b/.github/workflows/tests_gpu_cuda.yml @@ -119,13 +119,12 @@ jobs: - name: Install required packages run: | - python -m pip install ninja cmake scipy custatevec-cu${{ matrix.cuda_version }} + python -m pip install ninja cmake custatevec-cu${{ matrix.cuda_version }} sudo apt-get -y -q install liblapack-dev - name: Build and run unit tests run: | cd main - export SCIPY_LIBS=$(python -c "import site; print( f'{site.getsitepackages()[0]}/scipy.libs')") cmake . -BBuild \ -DCUQUANTUM_SDK=$(python -c "import site; print( f'{site.getsitepackages()[0]}/cuquantum')")\ -DBUILD_TESTS=ON \ diff --git a/.github/workflows/tests_gpu_kokkos.yml b/.github/workflows/tests_gpu_kokkos.yml index 19e51f51bf..90b50dfece 100644 --- a/.github/workflows/tests_gpu_kokkos.yml +++ b/.github/workflows/tests_gpu_kokkos.yml @@ -148,14 +148,11 @@ jobs: - name: Install dependencies run: | - sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION ninja-build gcovr lcov liblapack-dev - python -m pip install scipy - + sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION ninja-build gcovr lcov liblapack-dev - name: Build and run unit tests run: | cd main - export SCIPY_LIBS=$(python -c "import site; print( f'{site.getsitepackages()[0]}/scipy.libs')") cmake . -BBuild -G Ninja \ -DCMAKE_BUILD_TYPE=Debug \ -DBUILD_TESTS=ON \ diff --git a/.github/workflows/tests_linux.yml b/.github/workflows/tests_linux.yml index f99809eef6..eed205e4ca 100644 --- a/.github/workflows/tests_linux.yml +++ b/.github/workflows/tests_linux.yml @@ -43,9 +43,6 @@ jobs: with: python-version: '3.9' - - name: Get required Python packages - run: python -m pip install scipy - - name: Checkout PennyLane-Lightning uses: actions/checkout@v3 @@ -55,7 +52,6 @@ jobs: - name: Build and run unit tests run: | set -x - export SCIPY_LIBS=$(python -c "import site; print( f'{site.getsitepackages()[0]}/scipy.libs')") cmake . -BBuild -G Ninja \ -DCMAKE_BUILD_TYPE=Debug \ -DBUILD_TESTS=ON \ @@ -181,7 +177,6 @@ jobs: - name: Install backend device run: | cd main - python -m pip install scipy CMAKE_ARGS="-DPL_BACKEND=${{ matrix.pl_backend }} -DLQ_ENABLE_KERNEL_OMP=ON -DENABLE_PYTHON=ON -DCMAKE_CXX_COMPILER=$(which g++-$GCC_VERSION)" \ python -m pip install -e . -vv @@ -223,9 +218,6 @@ jobs: - name: Checkout PennyLane-Lightning uses: actions/checkout@v3 - - - name: Get required Python packages - run: python -m pip install scipy - name: Install dependencies run: sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION libopenblas-dev ninja-build gcovr lcov liblapack-dev @@ -233,7 +225,6 @@ jobs: - name: Build and run unit tests run: | set -x - export SCIPY_LIBS=$(python -c "import site; print( f'{site.getsitepackages()[0]}/scipy.libs')") cmake . -BBuild -G Ninja \ -DCMAKE_BUILD_TYPE=Debug \ -DENABLE_PYTHON=OFF \ @@ -302,9 +293,6 @@ jobs: name: Install Python with: python-version: '3.9' - - - name: Get required Python packages - run: python -m pip install scipy - name: Install dependencies run: sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION libopenblas-dev @@ -407,9 +395,6 @@ jobs: rm -rf Kokkos mkdir Kokkos/ cp -rf ${{ github.workspace}}/Kokkos_install/${{ matrix.exec_model }}/* Kokkos/ - - - name: Get required Python packages - run: python -m pip install scipy - name: Install dependencies run: sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION ninja-build gcovr lcov liblapack-dev @@ -417,7 +402,6 @@ jobs: - name: Build and run unit tests run: | set -x - export SCIPY_LIBS=$(python -c "import site; print( f'{site.getsitepackages()[0]}/scipy.libs')") cmake . -BBuild -G Ninja \ -DCMAKE_BUILD_TYPE=Debug \ -DBUILD_TESTS=ON \ @@ -685,7 +669,6 @@ jobs: - name: Build and run unit tests run: | set -x - export SCIPY_LIBS=$(python -c "import site; print( f'{site.getsitepackages()[0]}/scipy.libs')") cmake . -BBuild -G Ninja \ -DCMAKE_BUILD_TYPE=Debug \ -DBUILD_TESTS=ON \ diff --git a/.github/workflows/tests_linux_x86_mpi_gpu.yml b/.github/workflows/tests_linux_x86_mpi_gpu.yml index c1820365fe..ea1dd27e17 100644 --- a/.github/workflows/tests_linux_x86_mpi_gpu.yml +++ b/.github/workflows/tests_linux_x86_mpi_gpu.yml @@ -118,7 +118,6 @@ jobs: run: | source /etc/profile.d/modules.sh && module use /opt/modules/ && module load ${{ matrix.mpilib }}/cuda-${{ matrix.cuda_version_maj }}.${{ matrix.cuda_version_min }} export CUQUANTUM_SDK=$(python -c "import site; print( f'{site.getsitepackages()[0]}/cuquantum/lib')") - export SCIPY_LIBS=$(python -c "import site; print( f'{site.getsitepackages()[0]}/scipy.libs')") cmake . -BBuild \ -DPL_BACKEND=lightning_gpu \ -DENABLE_PYTHON=OFF \ From 81a114cc6c59a2c6d54e70f6729bb9a4aac3602f Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Tue, 26 Mar 2024 11:43:33 -0400 Subject: [PATCH 377/428] quick fix --- CMakeLists.txt | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e425bbc62d..1f0a248dc6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -95,11 +95,14 @@ if (CMAKE_SYSTEM_NAME MATCHES "Windows" OR CMAKE_SYSTEM_NAME MATCHES "Linux") message("Python site-packages directory (Windows): ${SCIPYLIBS}") endif() - if(NOT EXISTS ${SCIPYLIBS}) - message(FATAL_ERROR, "scipy.libs is not found.") - else() - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h) - endif() + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h) + + + #if(NOT EXISTS ${SCIPYLIBS}) + # message(FATAL_ERROR, "scipy.libs is not found.") + #else() + #configure_file(${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h) + #endif() else() set(SCIPYLIBS "/System/Library/Frameworks/Accelerate.framework/Versions/Current/Frameworks/vecLib.framework/libLAPACK.dylib") configure_file(${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h) From 5cf765b9b20446ef895bcc5b77b63c61f282cc42 Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Tue, 26 Mar 2024 11:45:07 -0400 Subject: [PATCH 378/428] Pinned cmake in workflows --- .github/workflows/tests_gpu_cuda.yml | 4 ++-- .github/workflows/tests_linux_x86_mpi_gpu.yml | 2 +- .github/workflows/tests_windows.yml | 2 +- requirements-dev.txt | 3 ++- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/tests_gpu_cuda.yml b/.github/workflows/tests_gpu_cuda.yml index 06741a1152..b7c5eaab6f 100644 --- a/.github/workflows/tests_gpu_cuda.yml +++ b/.github/workflows/tests_gpu_cuda.yml @@ -119,7 +119,7 @@ jobs: - name: Install required packages run: | - python -m pip install ninja cmake custatevec-cu${{ matrix.cuda_version }} + python -m pip install ninja "cmake!=3.29.0" custatevec-cu${{ matrix.cuda_version }} sudo apt-get -y -q install liblapack-dev - name: Build and run unit tests @@ -241,7 +241,7 @@ jobs: run: | cd main python -m pip install -r requirements-dev.txt - python -m pip install cmake custatevec-cu${{ matrix.cuda_version }} openfermionpyscf + python -m pip install "cmake!=3.29.0" custatevec-cu${{ matrix.cuda_version }} openfermionpyscf - name: Checkout PennyLane for release build if: inputs.pennylane-version == 'release' diff --git a/.github/workflows/tests_linux_x86_mpi_gpu.yml b/.github/workflows/tests_linux_x86_mpi_gpu.yml index 7af97b5046..555b327654 100644 --- a/.github/workflows/tests_linux_x86_mpi_gpu.yml +++ b/.github/workflows/tests_linux_x86_mpi_gpu.yml @@ -94,7 +94,7 @@ jobs: - name: Install required packages run: | python -m pip install -r requirements-dev.txt - python -m pip install cmake custatevec-cu12 + python -m pip install "cmake!=3.29.0" custatevec-cu12 sudo apt-get -y -q install liblapack-dev - name: Validate GPU version and installed compiler and modules diff --git a/.github/workflows/tests_windows.yml b/.github/workflows/tests_windows.yml index 395d4a8bdd..f6bf306e59 100644 --- a/.github/workflows/tests_windows.yml +++ b/.github/workflows/tests_windows.yml @@ -75,7 +75,7 @@ jobs: - name: Install dependencies if: steps.kokkos-cache.outputs.cache-hit != 'true' run: | - python -m pip install cmake build + python -m pip install "cmake!=3.29.0" build - name: Build Kokkos core library if: steps.kokkos-cache.outputs.cache-hit != 'true' diff --git a/requirements-dev.txt b/requirements-dev.txt index 474ef50d1f..949a7f1545 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -14,6 +14,7 @@ clang-tidy~=16.0 clang-format~=16.0 isort==5.13.2 click==8.0.4 -cmake==3.28.4 +# cmake 3.29.0 does not work with python <3.10 +cmake!=3.29.0 custatevec-cu12 pylint From c54af4574b913b8ee30839bdaff4919cea329071 Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Tue, 26 Mar 2024 11:46:10 -0400 Subject: [PATCH 379/428] Pinned cmake in more workflows --- .github/workflows/tests_windows.yml | 2 +- .github/workflows/wheel_macos_x86_64.yml | 2 +- .github/workflows/wheel_noarch.yml | 2 +- .github/workflows/wheel_win_x86_64.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/tests_windows.yml b/.github/workflows/tests_windows.yml index f6bf306e59..8fcaee8bd4 100644 --- a/.github/workflows/tests_windows.yml +++ b/.github/workflows/tests_windows.yml @@ -120,7 +120,7 @@ jobs: - name: Install dependencies if: steps.vcpkg-cache.outputs.cache-hit != 'true' run: | - python -m pip install cmake build ninja + python -m pip install "cmake!=3.29.0" build ninja - name: Build Lapack library if: steps.vcpkg-cache.outputs.cache-hit != 'true' diff --git a/.github/workflows/wheel_macos_x86_64.yml b/.github/workflows/wheel_macos_x86_64.yml index 7bcf05a793..07b11a4948 100644 --- a/.github/workflows/wheel_macos_x86_64.yml +++ b/.github/workflows/wheel_macos_x86_64.yml @@ -68,7 +68,7 @@ jobs: run: | mkdir -p ${{ github.workspace}}/Kokkos_install/${{ matrix.exec_model }} cd kokkos - python -m pip install cmake ninja + python -m pip install "cmake!=3.29.0" ninja cmake -BBuild . -DCMAKE_INSTALL_PREFIX=${{ github.workspace}}/Kokkos_install/${{ matrix.exec_model }} \ -DKokkos_ENABLE_COMPLEX_ALIGN=OFF \ diff --git a/.github/workflows/wheel_noarch.yml b/.github/workflows/wheel_noarch.yml index ed4bfbdff4..92563df524 100644 --- a/.github/workflows/wheel_noarch.yml +++ b/.github/workflows/wheel_noarch.yml @@ -44,7 +44,7 @@ jobs: - name: Install CMake and ninja run: | - python -m pip install --upgrade cmake ninja + python -m pip install --upgrade "cmake!=3.29.0" ninja - name: Build wheels if: ${{ matrix.pl_backend == 'lightning_qubit'}} diff --git a/.github/workflows/wheel_win_x86_64.yml b/.github/workflows/wheel_win_x86_64.yml index 88a9feba10..1357d12361 100644 --- a/.github/workflows/wheel_win_x86_64.yml +++ b/.github/workflows/wheel_win_x86_64.yml @@ -64,7 +64,7 @@ jobs: - name: Install dependencies if: steps.kokkos-cache.outputs.cache-hit != 'true' run: | - python -m pip install cmake build + python -m pip install "cmake!=3.29.0" build - name: Build Kokkos core library if: steps.kokkos-cache.outputs.cache-hit != 'true' From f2ab0a94bbc313a909d200c86e2c32b6c217e273 Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Tue, 26 Mar 2024 11:49:38 -0400 Subject: [PATCH 380/428] Trigger CI From 59a141189b3ef4c2245b30dfcaa772f506213ca9 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Tue, 26 Mar 2024 12:03:13 -0400 Subject: [PATCH 381/428] quick tests --- pennylane_lightning/core/src/utils/SharedLibLoader.hpp | 5 +++-- pennylane_lightning/core/src/utils/UtilLinearAlg.hpp | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pennylane_lightning/core/src/utils/SharedLibLoader.hpp b/pennylane_lightning/core/src/utils/SharedLibLoader.hpp index 4566901f50..892a3c7005 100644 --- a/pennylane_lightning/core/src/utils/SharedLibLoader.hpp +++ b/pennylane_lightning/core/src/utils/SharedLibLoader.hpp @@ -59,9 +59,10 @@ class SharedLibLoader final { PL_ABORT_IF(!handle_, dlerror()); } #elif defined(_MSC_VER) - // codefactor[skip] handle_ = LoadLibrary(filename.c_str()); - PL_ABORT_IF(!handle_, std::to_string(GetLastError())); + if (filename != "lapack.so") { + PL_ABORT_IF(!handle_, std::to_string(GetLastError())); + } #endif } diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index 05bb4f9df2..8664d69ef3 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -110,6 +110,7 @@ void compute_diagonalizing_gates(int n, int lda, #else std::shared_ptr blasLib = std::make_shared("lapack.so"); if (!blasLib->getHandle()) { + std::cout<<"++++++++++++test"<> blasLibs; std::string scipyPathStr(SCIPY_LIBS_PATH); @@ -195,5 +196,7 @@ void compute_diagonalizing_gates(int n, int lda, [](std::complex value) { return std::complex{value.real(), -value.imag()}; }); + + dlclose(handle); } } // namespace Pennylane::Util From b0e86374971dfc9f8b7549355ed16cee67882b3e Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Tue, 26 Mar 2024 12:03:50 -0400 Subject: [PATCH 382/428] quick fix --- pennylane_lightning/core/src/utils/UtilLinearAlg.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index 8664d69ef3..4557fab18f 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -197,6 +197,5 @@ void compute_diagonalizing_gates(int n, int lda, return std::complex{value.real(), -value.imag()}; }); - dlclose(handle); } } // namespace Pennylane::Util From 8f696ebbe1bba330c12c3939fc873991626e4f26 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Tue, 26 Mar 2024 13:50:48 -0400 Subject: [PATCH 383/428] quick fix --- .../core/src/utils/UtilLinearAlg.hpp | 30 +++++++------------ 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index 4557fab18f..96ef56eae2 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -108,28 +108,20 @@ void compute_diagonalizing_gates(int n, int lda, std::shared_ptr blasLib = std::make_shared(libName); #else - std::shared_ptr blasLib = std::make_shared("lapack.so"); - if (!blasLib->getHandle()) { - std::cout<<"++++++++++++test"<> blasLibs; + std::shared_ptr blasLib; + std::vector> blasLibs; - std::string scipyPathStr(SCIPY_LIBS_PATH); - - if (scipyPathStr.empty() || !std::filesystem::exists(scipyPathStr)) { - std::string currentPathStr(getPath()); - scipyPathStr = currentPathStr + "/../../scipy.libs"; - if (!std::filesystem::exists(scipyPathStr)) { - } - PL_ABORT_IF(!std::filesystem::exists(scipyPathStr), - "The scipy.libs/ is not available."); - } else { - std::exit(EXIT_FAILURE); - } + std::string scipyPathStr(SCIPY_LIBS_PATH); + if (!std::filesystem::exists(scipyPathStr)) { + std::string currentPathStr(getPath()); + scipyPathStr = currentPathStr + "/../../scipy.libs"; + } + if (!std::filesystem::exists(std::filesystem::canonical(scipyPathStr))) { + blasLib = std::make_shared("lapack.so"); + } else { std::filesystem::path scipyLibsPath(scipyPathStr); - std::cout << scipyLibsPath << std::endl; - std::vector> availableLibs; for (const auto &lib : @@ -155,6 +147,7 @@ void compute_diagonalizing_gates(int n, int lda, blasLibs.emplace_back( std::make_shared(libPathStr)); } + blasLib = blasLibs.back(); } #endif @@ -196,6 +189,5 @@ void compute_diagonalizing_gates(int n, int lda, [](std::complex value) { return std::complex{value.real(), -value.imag()}; }); - } } // namespace Pennylane::Util From 475a1dcbfb1a477c7e98f9028649ee8c391c4e56 Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Tue, 26 Mar 2024 14:07:36 -0400 Subject: [PATCH 384/428] Apply suggestions from code review Co-authored-by: Ali Asadi <10773383+maliasadi@users.noreply.github.com> --- .github/workflows/tests_gpu_cuda.yml | 6 +++++- .github/workflows/tests_linux_x86_mpi_gpu.yml | 2 ++ .github/workflows/tests_windows.yml | 4 ++-- .github/workflows/wheel_macos_x86_64.yml | 2 ++ .github/workflows/wheel_win_x86_64.yml | 2 ++ 5 files changed, 13 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests_gpu_cuda.yml b/.github/workflows/tests_gpu_cuda.yml index b7c5eaab6f..87caeaa4a5 100644 --- a/.github/workflows/tests_gpu_cuda.yml +++ b/.github/workflows/tests_gpu_cuda.yml @@ -119,7 +119,9 @@ jobs: - name: Install required packages run: | - python -m pip install ninja "cmake!=3.29.0" custatevec-cu${{ matrix.cuda_version }} + # Omitting the installation of cmake v3.29.0 due to + # https://github.com/scikit-build/cmake-python-distributions/pull/474 + python -m pip install ninja "cmake!=3.29.0" custatevec-cu${{ matrix.cuda_version }} sudo apt-get -y -q install liblapack-dev - name: Build and run unit tests @@ -241,6 +243,8 @@ jobs: run: | cd main python -m pip install -r requirements-dev.txt + # Omitting the installation of cmake v3.29.0 due to + # https://github.com/scikit-build/cmake-python-distributions/pull/474 python -m pip install "cmake!=3.29.0" custatevec-cu${{ matrix.cuda_version }} openfermionpyscf - name: Checkout PennyLane for release build diff --git a/.github/workflows/tests_linux_x86_mpi_gpu.yml b/.github/workflows/tests_linux_x86_mpi_gpu.yml index 555b327654..138b6c0cb2 100644 --- a/.github/workflows/tests_linux_x86_mpi_gpu.yml +++ b/.github/workflows/tests_linux_x86_mpi_gpu.yml @@ -94,6 +94,8 @@ jobs: - name: Install required packages run: | python -m pip install -r requirements-dev.txt + # Omitting the installation of cmake v3.29.0 due to + # https://github.com/scikit-build/cmake-python-distributions/pull/474 python -m pip install "cmake!=3.29.0" custatevec-cu12 sudo apt-get -y -q install liblapack-dev diff --git a/.github/workflows/tests_windows.yml b/.github/workflows/tests_windows.yml index 8fcaee8bd4..395d4a8bdd 100644 --- a/.github/workflows/tests_windows.yml +++ b/.github/workflows/tests_windows.yml @@ -75,7 +75,7 @@ jobs: - name: Install dependencies if: steps.kokkos-cache.outputs.cache-hit != 'true' run: | - python -m pip install "cmake!=3.29.0" build + python -m pip install cmake build - name: Build Kokkos core library if: steps.kokkos-cache.outputs.cache-hit != 'true' @@ -120,7 +120,7 @@ jobs: - name: Install dependencies if: steps.vcpkg-cache.outputs.cache-hit != 'true' run: | - python -m pip install "cmake!=3.29.0" build ninja + python -m pip install cmake build ninja - name: Build Lapack library if: steps.vcpkg-cache.outputs.cache-hit != 'true' diff --git a/.github/workflows/wheel_macos_x86_64.yml b/.github/workflows/wheel_macos_x86_64.yml index 07b11a4948..799eb21644 100644 --- a/.github/workflows/wheel_macos_x86_64.yml +++ b/.github/workflows/wheel_macos_x86_64.yml @@ -68,6 +68,8 @@ jobs: run: | mkdir -p ${{ github.workspace}}/Kokkos_install/${{ matrix.exec_model }} cd kokkos + # Omitting the installation of cmake v3.29.0 due to + # https://github.com/scikit-build/cmake-python-distributions/pull/474 python -m pip install "cmake!=3.29.0" ninja cmake -BBuild . -DCMAKE_INSTALL_PREFIX=${{ github.workspace}}/Kokkos_install/${{ matrix.exec_model }} \ diff --git a/.github/workflows/wheel_win_x86_64.yml b/.github/workflows/wheel_win_x86_64.yml index 1357d12361..43fd4b50c9 100644 --- a/.github/workflows/wheel_win_x86_64.yml +++ b/.github/workflows/wheel_win_x86_64.yml @@ -64,6 +64,8 @@ jobs: - name: Install dependencies if: steps.kokkos-cache.outputs.cache-hit != 'true' run: | + # Omitting the installation of cmake v3.29.0 due to + # https://github.com/scikit-build/cmake-python-distributions/pull/474 python -m pip install "cmake!=3.29.0" build - name: Build Kokkos core library From 0a901fc97c60b13041af08286c1b818a2536a48f Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Tue, 26 Mar 2024 14:13:54 -0400 Subject: [PATCH 385/428] quickfix --- CMakeLists.txt | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1f0a248dc6..39158f4a2c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -95,14 +95,9 @@ if (CMAKE_SYSTEM_NAME MATCHES "Windows" OR CMAKE_SYSTEM_NAME MATCHES "Linux") message("Python site-packages directory (Windows): ${SCIPYLIBS}") endif() - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h) - - - #if(NOT EXISTS ${SCIPYLIBS}) - # message(FATAL_ERROR, "scipy.libs is not found.") - #else() - #configure_file(${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h) - #endif() + if(EXISTS ${SCIPYLIBS}) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h) + endif() else() set(SCIPYLIBS "/System/Library/Frameworks/Accelerate.framework/Versions/Current/Frameworks/vecLib.framework/libLAPACK.dylib") configure_file(${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h) From a698448301c35b9de1732354933e7d951c8f1263 Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Tue, 26 Mar 2024 14:14:26 -0400 Subject: [PATCH 386/428] [skip ci] Apply suggestions from code review Co-authored-by: Ali Asadi <10773383+maliasadi@users.noreply.github.com> --- .github/workflows/wheel_noarch.yml | 2 ++ requirements-dev.txt | 3 +-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/wheel_noarch.yml b/.github/workflows/wheel_noarch.yml index 92563df524..2b69411788 100644 --- a/.github/workflows/wheel_noarch.yml +++ b/.github/workflows/wheel_noarch.yml @@ -44,6 +44,8 @@ jobs: - name: Install CMake and ninja run: | + # Omitting the installation of cmake v3.29.0 due to + # https://github.com/scikit-build/cmake-python-distributions/pull/474 python -m pip install --upgrade "cmake!=3.29.0" ninja - name: Build wheels diff --git a/requirements-dev.txt b/requirements-dev.txt index 949a7f1545..474ef50d1f 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -14,7 +14,6 @@ clang-tidy~=16.0 clang-format~=16.0 isort==5.13.2 click==8.0.4 -# cmake 3.29.0 does not work with python <3.10 -cmake!=3.29.0 +cmake==3.28.4 custatevec-cu12 pylint From 08e2d4a87d8adeeb1fddb924d29bacb1769df3ee Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Tue, 26 Mar 2024 14:21:02 -0400 Subject: [PATCH 387/428] Trigger CI From e2d6c6ad61ecfd17fa8fbc83a1b87007404f8aab Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Tue, 26 Mar 2024 14:23:53 -0400 Subject: [PATCH 388/428] update sharedlibloader --- pennylane_lightning/core/src/utils/SharedLibLoader.hpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/pennylane_lightning/core/src/utils/SharedLibLoader.hpp b/pennylane_lightning/core/src/utils/SharedLibLoader.hpp index 892a3c7005..0cbda8ae6a 100644 --- a/pennylane_lightning/core/src/utils/SharedLibLoader.hpp +++ b/pennylane_lightning/core/src/utils/SharedLibLoader.hpp @@ -55,14 +55,10 @@ class SharedLibLoader final { #if defined(__APPLE__) || defined(__linux__) handle_ = dlopen(filename.c_str(), rtld_flags); // This allows users to use pre-installed LAPACK package - if (filename != "lapack.so") { - PL_ABORT_IF(!handle_, dlerror()); - } + PL_ABORT_IF(!handle_, dlerror()); #elif defined(_MSC_VER) handle_ = LoadLibrary(filename.c_str()); - if (filename != "lapack.so") { - PL_ABORT_IF(!handle_, std::to_string(GetLastError())); - } + PL_ABORT_IF(!handle_, std::to_string(GetLastError())); #endif } From 54f6b47f46b51a013a89fa92f287adacdd4200c1 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Tue, 26 Mar 2024 14:29:13 -0400 Subject: [PATCH 389/428] remove canonical file --- pennylane_lightning/core/src/utils/UtilLinearAlg.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index 96ef56eae2..1be38f0a13 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -117,7 +117,7 @@ void compute_diagonalizing_gates(int n, int lda, std::string currentPathStr(getPath()); scipyPathStr = currentPathStr + "/../../scipy.libs"; } - if (!std::filesystem::exists(std::filesystem::canonical(scipyPathStr))) { + if (!std::filesystem::exists(scipyPathStr)) { blasLib = std::make_shared("lapack.so"); } else { std::filesystem::path scipyLibsPath(scipyPathStr); From 5f9d7b1744a842b1e040d6c9f4cd51b89d6b646c Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Tue, 26 Mar 2024 14:40:33 -0400 Subject: [PATCH 390/428] install scipy linux ci --- .github/workflows/tests_linux.yml | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/.github/workflows/tests_linux.yml b/.github/workflows/tests_linux.yml index eed205e4ca..ff35fad220 100644 --- a/.github/workflows/tests_linux.yml +++ b/.github/workflows/tests_linux.yml @@ -47,7 +47,9 @@ jobs: uses: actions/checkout@v3 - name: Install dependencies - run: echo ${{ github.event_name }} && sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION ninja-build gcovr lcov liblapack-dev + run: | + echo ${{ github.event_name }} && sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION ninja-build gcovr lcov liblapack-dev + python -m pip install scipy - name: Build and run unit tests run: | @@ -139,7 +141,9 @@ jobs: git status - name: Install dependencies - run: sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION liblapack-dev + run: | + sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION liblapack-dev + python -m pip install scipy - name: Get required Python packages run: | @@ -220,7 +224,9 @@ jobs: uses: actions/checkout@v3 - name: Install dependencies - run: sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION libopenblas-dev ninja-build gcovr lcov liblapack-dev + run: | + sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION libopenblas-dev ninja-build gcovr lcov liblapack-dev + python -m pip install scipy - name: Build and run unit tests run: | @@ -295,7 +301,9 @@ jobs: python-version: '3.9' - name: Install dependencies - run: sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION libopenblas-dev + run: | + sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION libopenblas-dev + python -m pip install scipy - name: Get required Python packages run: | @@ -397,7 +405,9 @@ jobs: cp -rf ${{ github.workspace}}/Kokkos_install/${{ matrix.exec_model }}/* Kokkos/ - name: Install dependencies - run: sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION ninja-build gcovr lcov liblapack-dev + run: | + sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION ninja-build gcovr lcov liblapack-dev + python -m pip install scipy - name: Build and run unit tests run: | @@ -492,7 +502,9 @@ jobs: pwd - name: Install dependencies - run: sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION liblapack-dev + run: | + sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION liblapack-dev + python -m pip install scipy - name: Get required Python packages run: | From 6c6259b2b2b4e9e77fca93a6e218fea7da04fb91 Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Tue, 26 Mar 2024 14:50:26 -0400 Subject: [PATCH 391/428] Fix indent. --- .github/workflows/tests_linux_x86_mpi_gpu.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests_linux_x86_mpi_gpu.yml b/.github/workflows/tests_linux_x86_mpi_gpu.yml index 138b6c0cb2..32dceb5c13 100644 --- a/.github/workflows/tests_linux_x86_mpi_gpu.yml +++ b/.github/workflows/tests_linux_x86_mpi_gpu.yml @@ -94,8 +94,8 @@ jobs: - name: Install required packages run: | python -m pip install -r requirements-dev.txt - # Omitting the installation of cmake v3.29.0 due to - # https://github.com/scikit-build/cmake-python-distributions/pull/474 + # Omitting the installation of cmake v3.29.0 due to + # https://github.com/scikit-build/cmake-python-distributions/pull/474 python -m pip install "cmake!=3.29.0" custatevec-cu12 sudo apt-get -y -q install liblapack-dev From f2fa464b92721fd20b9d8aef89c494e541180c69 Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Tue, 26 Mar 2024 15:28:53 -0400 Subject: [PATCH 392/428] Fix workflow files indentation --- .github/workflows/tests_gpu_cuda.yml | 10 +++++----- .github/workflows/wheel_macos_x86_64.yml | 4 ++-- .github/workflows/wheel_noarch.yml | 4 ++-- .github/workflows/wheel_win_x86_64.yml | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/tests_gpu_cuda.yml b/.github/workflows/tests_gpu_cuda.yml index 87caeaa4a5..d3abac6937 100644 --- a/.github/workflows/tests_gpu_cuda.yml +++ b/.github/workflows/tests_gpu_cuda.yml @@ -119,9 +119,9 @@ jobs: - name: Install required packages run: | - # Omitting the installation of cmake v3.29.0 due to - # https://github.com/scikit-build/cmake-python-distributions/pull/474 - python -m pip install ninja "cmake!=3.29.0" custatevec-cu${{ matrix.cuda_version }} + # Omitting the installation of cmake v3.29.0 due to + # https://github.com/scikit-build/cmake-python-distributions/pull/474 + python -m pip install ninja "cmake!=3.29.0" custatevec-cu${{ matrix.cuda_version }} sudo apt-get -y -q install liblapack-dev - name: Build and run unit tests @@ -243,8 +243,8 @@ jobs: run: | cd main python -m pip install -r requirements-dev.txt - # Omitting the installation of cmake v3.29.0 due to - # https://github.com/scikit-build/cmake-python-distributions/pull/474 + # Omitting the installation of cmake v3.29.0 due to + # https://github.com/scikit-build/cmake-python-distributions/pull/474 python -m pip install "cmake!=3.29.0" custatevec-cu${{ matrix.cuda_version }} openfermionpyscf - name: Checkout PennyLane for release build diff --git a/.github/workflows/wheel_macos_x86_64.yml b/.github/workflows/wheel_macos_x86_64.yml index 799eb21644..e3931dbcff 100644 --- a/.github/workflows/wheel_macos_x86_64.yml +++ b/.github/workflows/wheel_macos_x86_64.yml @@ -68,8 +68,8 @@ jobs: run: | mkdir -p ${{ github.workspace}}/Kokkos_install/${{ matrix.exec_model }} cd kokkos - # Omitting the installation of cmake v3.29.0 due to - # https://github.com/scikit-build/cmake-python-distributions/pull/474 + # Omitting the installation of cmake v3.29.0 due to + # https://github.com/scikit-build/cmake-python-distributions/pull/474 python -m pip install "cmake!=3.29.0" ninja cmake -BBuild . -DCMAKE_INSTALL_PREFIX=${{ github.workspace}}/Kokkos_install/${{ matrix.exec_model }} \ diff --git a/.github/workflows/wheel_noarch.yml b/.github/workflows/wheel_noarch.yml index 2b69411788..5de9cf6c5b 100644 --- a/.github/workflows/wheel_noarch.yml +++ b/.github/workflows/wheel_noarch.yml @@ -44,8 +44,8 @@ jobs: - name: Install CMake and ninja run: | - # Omitting the installation of cmake v3.29.0 due to - # https://github.com/scikit-build/cmake-python-distributions/pull/474 + # Omitting the installation of cmake v3.29.0 due to + # https://github.com/scikit-build/cmake-python-distributions/pull/474 python -m pip install --upgrade "cmake!=3.29.0" ninja - name: Build wheels diff --git a/.github/workflows/wheel_win_x86_64.yml b/.github/workflows/wheel_win_x86_64.yml index 43fd4b50c9..3d7e86f0e5 100644 --- a/.github/workflows/wheel_win_x86_64.yml +++ b/.github/workflows/wheel_win_x86_64.yml @@ -64,8 +64,8 @@ jobs: - name: Install dependencies if: steps.kokkos-cache.outputs.cache-hit != 'true' run: | - # Omitting the installation of cmake v3.29.0 due to - # https://github.com/scikit-build/cmake-python-distributions/pull/474 + # Omitting the installation of cmake v3.29.0 due to + # https://github.com/scikit-build/cmake-python-distributions/pull/474 python -m pip install "cmake!=3.29.0" build - name: Build Kokkos core library From 92e12031b5d218c11ffb947bc0ade732f306924d Mon Sep 17 00:00:00 2001 From: Vincent Michaud-Rioux Date: Tue, 26 Mar 2024 17:23:30 -0400 Subject: [PATCH 393/428] Comment triggers. (#662) * Comment triggers. * pytest -x * trigger ci * Do not skip comp for LQ. * Revert triggers * Revert changes --- .github/workflows/tests_linux_x86_mpi_gpu.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests_linux_x86_mpi_gpu.yml b/.github/workflows/tests_linux_x86_mpi_gpu.yml index 32dceb5c13..b491363bfc 100644 --- a/.github/workflows/tests_linux_x86_mpi_gpu.yml +++ b/.github/workflows/tests_linux_x86_mpi_gpu.yml @@ -242,7 +242,7 @@ jobs: source /etc/profile.d/modules.sh && module use /opt/modules/ && module load ${{ matrix.mpilib }}/cuda-${{ matrix.cuda_version_maj }}.${{ matrix.cuda_version_min }} python -m pip install -r requirements-dev.txt python -m pip install custatevec-cu${{ matrix.cuda_version_maj }} mpi4py openfermionpyscf - SKIP_COMPILATION=True PL_BACKEND=lightning_qubit python -m pip install -e . -vv + PL_BACKEND=lightning_qubit python -m pip install -e . -vv - name: Checkout PennyLane for release build if: inputs.pennylane-version == 'release' From fc3d5345c3a67fac12207fe36fd37ce8b445feec Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Tue, 26 Mar 2024 18:26:26 -0400 Subject: [PATCH 394/428] add pragma onceguard --- pennylane_lightning/core/src/utils/SharedLibLoader.hpp | 3 ++- pennylane_lightning/core/src/utils/UtilLinearAlg.hpp | 4 +--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/pennylane_lightning/core/src/utils/SharedLibLoader.hpp b/pennylane_lightning/core/src/utils/SharedLibLoader.hpp index 0cbda8ae6a..2494ed4aef 100644 --- a/pennylane_lightning/core/src/utils/SharedLibLoader.hpp +++ b/pennylane_lightning/core/src/utils/SharedLibLoader.hpp @@ -16,12 +16,13 @@ * @file * Dynamic shared library functions API wrapper. */ +#pragma once #include #if defined(__APPLE__) || defined(__linux__) #include #elif defined(_MSC_VER) -#include +#include #endif #include "Error.hpp" diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index 1be38f0a13..10cb081fa8 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -28,12 +28,10 @@ #include #include -#include - #if defined(__APPLE__) || defined(__linux__) #include #elif defined(_MSC_VER) -#include +#include #endif #include "SharedLibLoader.hpp" From 6d42dbe24e94fc5bb8dc1e518814c41b3320f8a0 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Tue, 26 Mar 2024 18:55:43 -0400 Subject: [PATCH 395/428] quick test --- .github/workflows/wheel_win_x86_64.yml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/.github/workflows/wheel_win_x86_64.yml b/.github/workflows/wheel_win_x86_64.yml index 0c6b057b64..576630a902 100644 --- a/.github/workflows/wheel_win_x86_64.yml +++ b/.github/workflows/wheel_win_x86_64.yml @@ -99,12 +99,7 @@ jobs: name: ${{ matrix.os }} - ${{ matrix.pl_backend }} (Python ${{ fromJson('{ "cp39-*":"3.9","cp310-*":"3.10","cp311-*":"3.11","cp312-*":"3.12" }')[matrix.cibw_build] }}) runs-on: ${{ matrix.os }} - steps: - - uses: actions/setup-python@v4 - name: Install Python - with: - python-version: '3.9' - + steps: - name: Restoring cached dependencies id: kokkos-cache uses: actions/cache@v3 @@ -132,7 +127,7 @@ jobs: # Python build settings CIBW_BEFORE_BUILD: | - python -m pip install pybind11 cmake~=3.24.0 build scipy + python -m pip install pybind11 cmake~=3.24.0 build #Temporarily commenting while solving problems to find binaries in CIBW tests. # CIBW_BEFORE_TEST: | @@ -141,13 +136,14 @@ jobs: # CIBW_TEST_COMMAND: | # pl-device-test --device=lightning.qubit --skip-ops -x --tb=short --no-flaky-report - CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014 + #CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014 CIBW_BUILD_VERBOSITY: 3 CIBW_BUILD_FRONTEND: build run: | + python -m pip install scipy python -m cibuildwheel --output-dir wheelhouse - name: Patch wheels From 5202cb5fcdb9179facd0b0441f56d514eb3532a2 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Tue, 26 Mar 2024 19:29:20 -0400 Subject: [PATCH 396/428] remove tests on win32 --- .../tests/Test_MeasurementsBase.cpp | 11 ++++++- .../core/src/observables/Observables.hpp | 11 +++++++ .../observables/tests/Test_Observables.cpp | 22 ++++++++++++++ .../core/src/utils/CMakeLists.txt | 6 ++-- .../core/src/utils/SharedLibLoader.hpp | 29 +++++++++++-------- .../core/src/utils/UtilLinearAlg.hpp | 26 ++++++++++------- 6 files changed, 78 insertions(+), 27 deletions(-) diff --git a/pennylane_lightning/core/src/measurements/tests/Test_MeasurementsBase.cpp b/pennylane_lightning/core/src/measurements/tests/Test_MeasurementsBase.cpp index a559773a7f..7c3fe1d015 100644 --- a/pennylane_lightning/core/src/measurements/tests/Test_MeasurementsBase.cpp +++ b/pennylane_lightning/core/src/measurements/tests/Test_MeasurementsBase.cpp @@ -570,6 +570,7 @@ TEST_CASE("Expval Shot- NamedObs", "[MeasurementsBase][Observables]") { } } +#ifndef _MSC_VER template void testHermitianObsExpvalShot() { if constexpr (!std::is_same_v) { using StateVectorT = typename TypeList::Type; @@ -672,6 +673,7 @@ TEST_CASE("Expval Shot - HermitianObs ", "[MeasurementsBase][Observables]") { testHermitianObsExpvalShot(); } } +#endif template void testHermitianObsExpval() { if constexpr (!std::is_same_v) { @@ -837,6 +839,7 @@ template void testTensorProdObsExpvalShot() { expected, static_cast(0.20))); } +#ifndef _MSC_VER DYNAMIC_SECTION(" With Identity and shots_range" << StateVectorToName::name) { size_t num_shots = 80000; @@ -857,6 +860,7 @@ template void testTensorProdObsExpvalShot() { REQUIRE_THAT(result, Catch::Matchers::WithinRel( expected, static_cast(0.20))); } +#endif testTensorProdObsExpvalShot(); } @@ -1007,6 +1011,7 @@ TEST_CASE("Var - HermitianObs", "[MeasurementsBase][Observables]") { } } +#ifndef _MSC_VER template void testHermitianObsShotVar() { if constexpr (!std::is_same_v) { using StateVectorT = typename TypeList::Type; @@ -1091,6 +1096,7 @@ TEST_CASE("Var - HermitianObs Shot", "[MeasurementsBase][Observables]") { testHermitianObsShotVar(); } } +#endif template void testTensorProdObsVarShot() { if constexpr (!std::is_same_v) { @@ -1141,6 +1147,7 @@ template void testTensorProdObsVarShot() { expected, static_cast(0.20))); } +#ifndef _MSC_VER DYNAMIC_SECTION("With Hermitian and NameObs" << StateVectorToName::name) { using MatrixT = std::vector; @@ -1190,6 +1197,7 @@ template void testTensorProdObsVarShot() { REQUIRE_THAT(result, Catch::Matchers::WithinRel( expected, static_cast(0.20))); } +#endif DYNAMIC_SECTION(" full wires with apply operations" << StateVectorToName::name) { @@ -1547,7 +1555,7 @@ template void testHamiltonianObsExpvalShot() { REQUIRE_THAT(res, Catch::Matchers::WithinRel( expected, static_cast(0.20))); } - +#ifndef _MSC_VER DYNAMIC_SECTION("YHer" << StateVectorToName::name) { auto Y0 = std::make_shared>( "PauliY", std::vector{0}); @@ -1568,6 +1576,7 @@ template void testHamiltonianObsExpvalShot() { REQUIRE_THAT(res, Catch::Matchers::WithinRel( expected, static_cast(0.20))); } +#endif testHamiltonianObsExpvalShot(); } diff --git a/pennylane_lightning/core/src/observables/Observables.hpp b/pennylane_lightning/core/src/observables/Observables.hpp index 5e8ba3831f..9585ed0142 100644 --- a/pennylane_lightning/core/src/observables/Observables.hpp +++ b/pennylane_lightning/core/src/observables/Observables.hpp @@ -23,7 +23,9 @@ #include "Error.hpp" #include "Util.hpp" +#ifndef _MSC_VER #include "UtilLinearAlg.hpp" +#endif namespace Pennylane::Observables { /** @@ -217,8 +219,11 @@ class HermitianObsBase : public Observable { protected: MatrixT matrix_; std::vector wires_; + +#ifndef _MSC_VER std::vector eigenVals_; MatrixT unitary_; +#endif private: [[nodiscard]] auto isEqual(const Observable &other) const @@ -240,6 +245,7 @@ class HermitianObsBase : public Observable { : matrix_{std::move(matrix)}, wires_{std::move(wires)} { PL_ASSERT(matrix_.size() == Util::exp2(2 * wires_.size())); + #ifndef _MSC_VER std::vector> mat(matrix_.size()); std::transform(matrix_.begin(), matrix_.end(), mat.begin(), @@ -257,6 +263,7 @@ class HermitianObsBase : public Observable { std::transform( unitary.begin(), unitary.end(), unitary_.begin(), [](ComplexT value) { return static_cast(value); }); + #endif } [[nodiscard]] auto getMatrix() const -> const MatrixT & { return matrix_; } @@ -277,6 +284,7 @@ class HermitianObsBase : public Observable { [[maybe_unused]] StateVectorT &sv, [[maybe_unused]] std::vector> &eigenValues, [[maybe_unused]] std::vector &ob_wires) const override { +#ifndef _MSC_VER std::vector> mat(matrix_.size()); std::transform(matrix_.begin(), matrix_.end(), mat.begin(), @@ -294,6 +302,9 @@ class HermitianObsBase : public Observable { ob_wires = wires_; sv.applyMatrix(unitary_, wires_); eigenValues.push_back(eigenVals_); +#else + PL_ABORT("Hermitian observables do not support shot measurement for Windows."); +#endif } }; diff --git a/pennylane_lightning/core/src/observables/tests/Test_Observables.cpp b/pennylane_lightning/core/src/observables/tests/Test_Observables.cpp index 98f9b175f3..b3b07cb746 100644 --- a/pennylane_lightning/core/src/observables/tests/Test_Observables.cpp +++ b/pennylane_lightning/core/src/observables/tests/Test_Observables.cpp @@ -207,6 +207,7 @@ template void testHermitianObsBase() { REQUIRE(ob2 != ob3); } +#ifndef _MSC_VER DYNAMIC_SECTION("Failed to create a HermitianObs- " << StateVectorToName::name) { std::mt19937_64 re{1337}; @@ -226,6 +227,27 @@ template void testHermitianObsBase() { Catch::Matchers::Contains("The matrix passed to HermitianObs " "is not a Hermitian matrix.")); } +#else + DYNAMIC_SECTION("Failed for HermitianObs for applyInPlaceShots - " + << StateVectorToName::name) { + std::mt19937_64 re{1337}; + constexpr size_t num_qubits = 3; + auto init_state = + createRandomStateVectorData(re, num_qubits); + + StateVectorT state_vector(init_state.data(), init_state.size()); + auto obs = + HermitianObsT{std::vector{1.0, 0.0, -1.0, 0.0}, {0}}; + + std::vector> eigenValues; + std::vector ob_wires; + + REQUIRE_THROWS_WITH( + obs.applyInPlaceShots(state_vector, eigenValues, ob_wires), + Catch::Matchers::Contains( + "Hermitian observables do not support shot measurement for Windows.")); + } +#endif testHermitianObsBase(); } diff --git a/pennylane_lightning/core/src/utils/CMakeLists.txt b/pennylane_lightning/core/src/utils/CMakeLists.txt index 9b0775c946..794e9adae6 100644 --- a/pennylane_lightning/core/src/utils/CMakeLists.txt +++ b/pennylane_lightning/core/src/utils/CMakeLists.txt @@ -2,13 +2,13 @@ cmake_minimum_required(VERSION 3.20) project(lightning_utils LANGUAGES CXX) -set(LQUBIT_UTILS_FILES RuntimeInfo.cpp UtilLinearAlg.cpp CACHE INTERNAL "" FORCE) +set(LQUBIT_UTILS_FILES RuntimeInfo.cpp CACHE INTERNAL "" FORCE) add_library(lightning_utils STATIC ${LQUBIT_UTILS_FILES}) -if(ENABLE_PYTHON) - target_link_libraries(lightning_compile_options INTERFACE "-D_ENABLE_PYTHON=1") +if(NOT WIN32) + list(APPEND LQUBIT_UTILS_FILES UtilLinearAlg.cpp) endif() target_include_directories(lightning_utils INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/pennylane_lightning/core/src/utils/SharedLibLoader.hpp b/pennylane_lightning/core/src/utils/SharedLibLoader.hpp index 2494ed4aef..44d76fbff9 100644 --- a/pennylane_lightning/core/src/utils/SharedLibLoader.hpp +++ b/pennylane_lightning/core/src/utils/SharedLibLoader.hpp @@ -21,8 +21,9 @@ #if defined(__APPLE__) || defined(__linux__) #include -#elif defined(_MSC_VER) -#include +//TODO add windows support +//#elif defined(_MSC_VER) +//#include #endif #include "Error.hpp" @@ -40,8 +41,9 @@ class SharedLibLoader final { private: #if defined(__APPLE__) || defined(__linux__) void *handle_{nullptr}; -#elif defined(_MSC_VER) - HMODULE handle_{nullptr}; +//TODO add windows support +//#elif defined(_MSC_VER) +// HMODULE handle_{nullptr}; #endif public: @@ -57,17 +59,19 @@ class SharedLibLoader final { handle_ = dlopen(filename.c_str(), rtld_flags); // This allows users to use pre-installed LAPACK package PL_ABORT_IF(!handle_, dlerror()); -#elif defined(_MSC_VER) - handle_ = LoadLibrary(filename.c_str()); - PL_ABORT_IF(!handle_, std::to_string(GetLastError())); +//TODO add windows support +//#elif defined(_MSC_VER) +// handle_ = LoadLibrary(filename.c_str()); +// PL_ABORT_IF(!handle_, std::to_string(GetLastError())); #endif } ~SharedLibLoader() { #if defined(__APPLE__) || defined(__linux__) dlclose(handle_); -#elif defined(_MSC_VER) - FreeLibrary(handle_); +//TODO add windows support +//#elif defined(_MSC_VER) +// FreeLibrary(handle_); #endif } @@ -77,9 +81,10 @@ class SharedLibLoader final { #if defined(__APPLE__) || defined(__linux__) void *sym = dlsym(handle_, symbol.c_str()); PL_ABORT_IF(!sym, dlerror()); -#elif defined(_MSC_VER) - void *sym = GetProcAddress(handle_, symbol.c_str()); - PL_ABORT_IF(!handle_, std::to_string(GetLastError())); +//TODO add windows support +//#elif defined(_MSC_VER) +// void *sym = GetProcAddress(handle_, symbol.c_str()); +// PL_ABORT_IF(!handle_, std::to_string(GetLastError())); #endif return sym; } diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index 10cb081fa8..d1b3d76330 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -30,8 +30,9 @@ #if defined(__APPLE__) || defined(__linux__) #include -#elif defined(_MSC_VER) -#include +//TODO add windows support +//#elif defined(_MSC_VER) +//#include #endif #include "SharedLibLoader.hpp" @@ -62,14 +63,15 @@ inline const char *getPath() { "Can't get the path to the shared library."); return dl_info.dli_fname; } -#elif defined(_MSC_VER) -inline std::string getPath() { - char buffer[MAX_PATH]; - GetModuleFileName(nullptr, buffer, MAX_PATH); - std::string fullPath(buffer); - std::size_t pos = fullPath.find_last_of("\\/"); - return fullPath.substr(0, pos); -} +//TODO add windows support +//#elif defined(_MSC_VER) +//inline std::string getPath() { +// char buffer[MAX_PATH]; +// GetModuleFileName(nullptr, buffer, MAX_PATH); +// std::string fullPath(buffer); +// std::size_t pos = fullPath.find_last_of("\\/"); +// return fullPath.substr(0, pos); +//} #endif /** @@ -116,7 +118,9 @@ void compute_diagonalizing_gates(int n, int lda, scipyPathStr = currentPathStr + "/../../scipy.libs"; } if (!std::filesystem::exists(scipyPathStr)) { - blasLib = std::make_shared("lapack.so"); + #ifdef __linux__ + blasLib = std::make_shared("lapack.so"); + #endif } else { std::filesystem::path scipyLibsPath(scipyPathStr); From e8438c87fa64c3e7dd2ad622205eca2ad935ef2e Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Tue, 26 Mar 2024 19:32:09 -0400 Subject: [PATCH 397/428] make format --- .../core/src/observables/Observables.hpp | 9 ++--- .../observables/tests/Test_Observables.cpp | 3 +- .../core/src/utils/SharedLibLoader.hpp | 34 +++++++++---------- .../core/src/utils/UtilLinearAlg.hpp | 30 ++++++++-------- 4 files changed, 39 insertions(+), 37 deletions(-) diff --git a/pennylane_lightning/core/src/observables/Observables.hpp b/pennylane_lightning/core/src/observables/Observables.hpp index 9585ed0142..2a4697d937 100644 --- a/pennylane_lightning/core/src/observables/Observables.hpp +++ b/pennylane_lightning/core/src/observables/Observables.hpp @@ -245,7 +245,7 @@ class HermitianObsBase : public Observable { : matrix_{std::move(matrix)}, wires_{std::move(wires)} { PL_ASSERT(matrix_.size() == Util::exp2(2 * wires_.size())); - #ifndef _MSC_VER +#ifndef _MSC_VER std::vector> mat(matrix_.size()); std::transform(matrix_.begin(), matrix_.end(), mat.begin(), @@ -263,7 +263,7 @@ class HermitianObsBase : public Observable { std::transform( unitary.begin(), unitary.end(), unitary_.begin(), [](ComplexT value) { return static_cast(value); }); - #endif +#endif } [[nodiscard]] auto getMatrix() const -> const MatrixT & { return matrix_; } @@ -284,7 +284,7 @@ class HermitianObsBase : public Observable { [[maybe_unused]] StateVectorT &sv, [[maybe_unused]] std::vector> &eigenValues, [[maybe_unused]] std::vector &ob_wires) const override { -#ifndef _MSC_VER +#ifndef _MSC_VER std::vector> mat(matrix_.size()); std::transform(matrix_.begin(), matrix_.end(), mat.begin(), @@ -303,7 +303,8 @@ class HermitianObsBase : public Observable { sv.applyMatrix(unitary_, wires_); eigenValues.push_back(eigenVals_); #else - PL_ABORT("Hermitian observables do not support shot measurement for Windows."); + PL_ABORT("Hermitian observables do not support shot measurement for " + "Windows."); #endif } }; diff --git a/pennylane_lightning/core/src/observables/tests/Test_Observables.cpp b/pennylane_lightning/core/src/observables/tests/Test_Observables.cpp index b3b07cb746..f90c17eff0 100644 --- a/pennylane_lightning/core/src/observables/tests/Test_Observables.cpp +++ b/pennylane_lightning/core/src/observables/tests/Test_Observables.cpp @@ -245,7 +245,8 @@ template void testHermitianObsBase() { REQUIRE_THROWS_WITH( obs.applyInPlaceShots(state_vector, eigenValues, ob_wires), Catch::Matchers::Contains( - "Hermitian observables do not support shot measurement for Windows.")); + "Hermitian observables do not support shot measurement for " + "Windows.")); } #endif diff --git a/pennylane_lightning/core/src/utils/SharedLibLoader.hpp b/pennylane_lightning/core/src/utils/SharedLibLoader.hpp index 44d76fbff9..cab9d51533 100644 --- a/pennylane_lightning/core/src/utils/SharedLibLoader.hpp +++ b/pennylane_lightning/core/src/utils/SharedLibLoader.hpp @@ -21,9 +21,9 @@ #if defined(__APPLE__) || defined(__linux__) #include -//TODO add windows support -//#elif defined(_MSC_VER) -//#include +// TODO add windows support +// #elif defined(_MSC_VER) +// #include #endif #include "Error.hpp" @@ -41,9 +41,9 @@ class SharedLibLoader final { private: #if defined(__APPLE__) || defined(__linux__) void *handle_{nullptr}; -//TODO add windows support -//#elif defined(_MSC_VER) -// HMODULE handle_{nullptr}; +// TODO add windows support +// #elif defined(_MSC_VER) +// HMODULE handle_{nullptr}; #endif public: @@ -59,19 +59,19 @@ class SharedLibLoader final { handle_ = dlopen(filename.c_str(), rtld_flags); // This allows users to use pre-installed LAPACK package PL_ABORT_IF(!handle_, dlerror()); -//TODO add windows support -//#elif defined(_MSC_VER) -// handle_ = LoadLibrary(filename.c_str()); -// PL_ABORT_IF(!handle_, std::to_string(GetLastError())); +// TODO add windows support +// #elif defined(_MSC_VER) +// handle_ = LoadLibrary(filename.c_str()); +// PL_ABORT_IF(!handle_, std::to_string(GetLastError())); #endif } ~SharedLibLoader() { #if defined(__APPLE__) || defined(__linux__) dlclose(handle_); -//TODO add windows support -//#elif defined(_MSC_VER) -// FreeLibrary(handle_); +// TODO add windows support +// #elif defined(_MSC_VER) +// FreeLibrary(handle_); #endif } @@ -81,10 +81,10 @@ class SharedLibLoader final { #if defined(__APPLE__) || defined(__linux__) void *sym = dlsym(handle_, symbol.c_str()); PL_ABORT_IF(!sym, dlerror()); -//TODO add windows support -//#elif defined(_MSC_VER) -// void *sym = GetProcAddress(handle_, symbol.c_str()); -// PL_ABORT_IF(!handle_, std::to_string(GetLastError())); +// TODO add windows support +// #elif defined(_MSC_VER) +// void *sym = GetProcAddress(handle_, symbol.c_str()); +// PL_ABORT_IF(!handle_, std::to_string(GetLastError())); #endif return sym; } diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index d1b3d76330..00fbdfe17b 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -30,9 +30,9 @@ #if defined(__APPLE__) || defined(__linux__) #include -//TODO add windows support -//#elif defined(_MSC_VER) -//#include +// TODO add windows support +// #elif defined(_MSC_VER) +// #include #endif #include "SharedLibLoader.hpp" @@ -63,15 +63,15 @@ inline const char *getPath() { "Can't get the path to the shared library."); return dl_info.dli_fname; } -//TODO add windows support -//#elif defined(_MSC_VER) -//inline std::string getPath() { -// char buffer[MAX_PATH]; -// GetModuleFileName(nullptr, buffer, MAX_PATH); -// std::string fullPath(buffer); -// std::size_t pos = fullPath.find_last_of("\\/"); -// return fullPath.substr(0, pos); -//} +// TODO add windows support +// #elif defined(_MSC_VER) +// inline std::string getPath() { +// char buffer[MAX_PATH]; +// GetModuleFileName(nullptr, buffer, MAX_PATH); +// std::string fullPath(buffer); +// std::size_t pos = fullPath.find_last_of("\\/"); +// return fullPath.substr(0, pos); +// } #endif /** @@ -118,9 +118,9 @@ void compute_diagonalizing_gates(int n, int lda, scipyPathStr = currentPathStr + "/../../scipy.libs"; } if (!std::filesystem::exists(scipyPathStr)) { - #ifdef __linux__ - blasLib = std::make_shared("lapack.so"); - #endif +#ifdef __linux__ + blasLib = std::make_shared("lapack.so"); +#endif } else { std::filesystem::path scipyLibsPath(scipyPathStr); From 57be532f6aa141d1d1e396706fa768a79e813fe5 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Tue, 26 Mar 2024 19:42:09 -0400 Subject: [PATCH 398/428] tidy up code --- .github/workflows/wheel_win_x86_64.yml | 10 ++++------ CMakeLists.txt | 11 +++++----- Makefile | 1 - .../core/src/utils/CMakeLists.txt | 3 ++- .../core/src/utils/UtilLinearAlg.hpp | 5 +++++ pennylane_lightning/core/src/utils/config.h | 19 ++++++++++++++++++ .../core/src/utils/config.h.in | 20 +++++++++++++++++++ 7 files changed, 55 insertions(+), 14 deletions(-) diff --git a/.github/workflows/wheel_win_x86_64.yml b/.github/workflows/wheel_win_x86_64.yml index 576630a902..530b6c6fc9 100644 --- a/.github/workflows/wheel_win_x86_64.yml +++ b/.github/workflows/wheel_win_x86_64.yml @@ -91,7 +91,7 @@ jobs: matrix: os: [windows-2022] arch: [AMD64] - pl_backend: ["lightning_qubit"] + pl_backend: ["lightning_kokkos", "lightning_qubit"] cibw_build: ${{ fromJson(needs.set_wheel_build_matrix.outputs.python_version) }} exec_model: [\"SERIAL\"] kokkos_version: ${{ fromJson(needs.set_wheel_build_matrix.outputs.kokkos_version) }} @@ -99,7 +99,7 @@ jobs: name: ${{ matrix.os }} - ${{ matrix.pl_backend }} (Python ${{ fromJson('{ "cp39-*":"3.9","cp310-*":"3.10","cp311-*":"3.11","cp312-*":"3.12" }')[matrix.cibw_build] }}) runs-on: ${{ matrix.os }} - steps: + steps: - name: Restoring cached dependencies id: kokkos-cache uses: actions/cache@v3 @@ -136,15 +136,13 @@ jobs: # CIBW_TEST_COMMAND: | # pl-device-test --device=lightning.qubit --skip-ops -x --tb=short --no-flaky-report - #CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014 + CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014 CIBW_BUILD_VERBOSITY: 3 CIBW_BUILD_FRONTEND: build - run: | - python -m pip install scipy - python -m cibuildwheel --output-dir wheelhouse + run: python -m cibuildwheel --output-dir wheelhouse - name: Patch wheels run: | diff --git a/CMakeLists.txt b/CMakeLists.txt index 39158f4a2c..f375de584f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -88,12 +88,11 @@ message("Python site-packages directory: ${Python_SITELIB}") if (CMAKE_SYSTEM_NAME MATCHES "Windows" OR CMAKE_SYSTEM_NAME MATCHES "Linux") set(SCIPYLIBS ${Python_SITELIB}/scipy.libs) - message("Python site-packages directory (Windows): ${SCIPYLIBS}") - - if (CMAKE_SYSTEM_NAME MATCHES "Windows") - string(REPLACE "\\" "/" SCIPYLIBS "${SCIPYLIBS}") - message("Python site-packages directory (Windows): ${SCIPYLIBS}") - endif() + #TODO add windows support + #if (CMAKE_SYSTEM_NAME MATCHES "Windows") + # string(REPLACE "\\" "/" SCIPYLIBS "${SCIPYLIBS}") + # message("Python site-packages directory (Windows): ${SCIPYLIBS}") + #endif() if(EXISTS ${SCIPYLIBS}) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h) diff --git a/Makefile b/Makefile index f9e75fab82..3d40597db9 100644 --- a/Makefile +++ b/Makefile @@ -96,7 +96,6 @@ test-cpp: rm -rf ./BuildTests cmake -BBuildTests -G Ninja \ -DCMAKE_BUILD_TYPE=Debug \ - -DENABLE_PYTHON=OFF \ -DBUILD_TESTS=ON \ -DENABLE_WARNINGS=ON \ -DPL_BACKEND=$(PL_BACKEND) \ diff --git a/pennylane_lightning/core/src/utils/CMakeLists.txt b/pennylane_lightning/core/src/utils/CMakeLists.txt index 794e9adae6..22a3811467 100644 --- a/pennylane_lightning/core/src/utils/CMakeLists.txt +++ b/pennylane_lightning/core/src/utils/CMakeLists.txt @@ -13,7 +13,8 @@ endif() target_include_directories(lightning_utils INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(lightning_utils INTERFACE lightning_compile_options - lightning_external_libs) + lightning_external_libs + ) set_property(TARGET lightning_utils PROPERTY POSITION_INDEPENDENT_CODE ON) diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index 00fbdfe17b..5bf150f736 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -57,6 +57,11 @@ std::unordered_map priority_lib = { namespace Pennylane::Util { #ifdef __linux__ +/** + * @brief Get the path to the current shared library object. + * + * @return const char* + */ inline const char *getPath() { Dl_info dl_info; PL_ABORT_IF(dladdr((const void *)getPath, &dl_info) == 0, diff --git a/pennylane_lightning/core/src/utils/config.h b/pennylane_lightning/core/src/utils/config.h index 877d4b943a..31acc1ca39 100644 --- a/pennylane_lightning/core/src/utils/config.h +++ b/pennylane_lightning/core/src/utils/config.h @@ -1,3 +1,22 @@ +// Copyright 2018-2024 Xanadu Quantum Technologies Inc. + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 + +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @file + * Record the path to scipy.libs at compile time. + */ + #ifndef CONFIG_H #define CONFIG_H #define SCIPY_LIBS_PATH "" diff --git a/pennylane_lightning/core/src/utils/config.h.in b/pennylane_lightning/core/src/utils/config.h.in index d8a0fd1a7f..5d4938ead5 100644 --- a/pennylane_lightning/core/src/utils/config.h.in +++ b/pennylane_lightning/core/src/utils/config.h.in @@ -1,3 +1,23 @@ +// Copyright 2018-2024 Xanadu Quantum Technologies Inc. + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 + +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @file + * Config file for the path to scipy.libs at compile time. + */ + + #ifndef CONFIG_H #define CONFIG_H #define SCIPY_LIBS_PATH "${SCIPYLIBS}" From 19d34f12cbcc7a7c0832141f0419f551e095b636 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Wed, 27 Mar 2024 12:19:04 -0400 Subject: [PATCH 399/428] use absolute path for scipy.libs --- .../core/src/utils/UtilLinearAlg.hpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index 5bf150f736..5e3c47634e 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -119,12 +119,23 @@ void compute_diagonalizing_gates(int n, int lda, std::string scipyPathStr(SCIPY_LIBS_PATH); if (!std::filesystem::exists(scipyPathStr)) { - std::string currentPathStr(getPath()); - scipyPathStr = currentPathStr + "/../../scipy.libs"; + [[maybe_unused]] std::string currentPathStr(getPath()); + [[maybe_unused]] scipyPathStr = currentPathStr + "/../../scipy.libs"; + + try { + // convert the relative path to absolute path + [[maybe_unused]] scipyPathStr = + std::filesystem::canonical(scipyPathStr).string(); + } catch (const std::exception &err) { + std::cout << "Canonical path for scipy.libs" + << " threw exception:\n" + << err.what() << '\n'; + } } if (!std::filesystem::exists(scipyPathStr)) { #ifdef __linux__ - blasLib = std::make_shared("lapack.so"); + [[maybe_unused]] blasLib = + std::make_shared("lapack.so"); #endif } else { std::filesystem::path scipyLibsPath(scipyPathStr); From 5dbd14f2e6daf3358aeb461471d7291ab2f2eb46 Mon Sep 17 00:00:00 2001 From: Dev version update bot Date: Wed, 27 Mar 2024 16:19:24 +0000 Subject: [PATCH 400/428] Auto update version --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index 0ae493ae06..58da0aa862 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.36.0-dev19" +__version__ = "0.36.0-dev20" From 4fb27dc8ff3096f1ed337afe2fd02a28451e84c1 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Wed, 27 Mar 2024 12:23:31 -0400 Subject: [PATCH 401/428] fix typo --- pennylane_lightning/core/src/utils/UtilLinearAlg.hpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index 5e3c47634e..ac97da7afd 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -28,6 +28,8 @@ #include #include +#include + #if defined(__APPLE__) || defined(__linux__) #include // TODO add windows support @@ -119,13 +121,12 @@ void compute_diagonalizing_gates(int n, int lda, std::string scipyPathStr(SCIPY_LIBS_PATH); if (!std::filesystem::exists(scipyPathStr)) { - [[maybe_unused]] std::string currentPathStr(getPath()); - [[maybe_unused]] scipyPathStr = currentPathStr + "/../../scipy.libs"; + std::string currentPathStr(getPath()); + scipyPathStr = currentPathStr + "/../../scipy.libs"; try { // convert the relative path to absolute path - [[maybe_unused]] scipyPathStr = - std::filesystem::canonical(scipyPathStr).string(); + scipyPathStr = std::filesystem::canonical(scipyPathStr).string(); } catch (const std::exception &err) { std::cout << "Canonical path for scipy.libs" << " threw exception:\n" @@ -134,8 +135,7 @@ void compute_diagonalizing_gates(int n, int lda, } if (!std::filesystem::exists(scipyPathStr)) { #ifdef __linux__ - [[maybe_unused]] blasLib = - std::make_shared("lapack.so"); + blasLib = std::make_shared("lapack.so"); #endif } else { std::filesystem::path scipyLibsPath(scipyPathStr); From a17334d8e57faff9439a4b677335d28bbfcb2352 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Wed, 27 Mar 2024 12:31:20 -0400 Subject: [PATCH 402/428] test for lapack is not available --- .github/workflows/tests_linux.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/tests_linux.yml b/.github/workflows/tests_linux.yml index ff35fad220..cf8d8ffc7e 100644 --- a/.github/workflows/tests_linux.yml +++ b/.github/workflows/tests_linux.yml @@ -48,7 +48,7 @@ jobs: - name: Install dependencies run: | - echo ${{ github.event_name }} && sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION ninja-build gcovr lcov liblapack-dev + echo ${{ github.event_name }} && sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION ninja-build gcovr lcov python -m pip install scipy - name: Build and run unit tests @@ -142,7 +142,7 @@ jobs: - name: Install dependencies run: | - sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION liblapack-dev + sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION python -m pip install scipy - name: Get required Python packages @@ -225,7 +225,7 @@ jobs: - name: Install dependencies run: | - sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION libopenblas-dev ninja-build gcovr lcov liblapack-dev + sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION libopenblas-dev ninja-build gcovr lcov python -m pip install scipy - name: Build and run unit tests @@ -406,7 +406,7 @@ jobs: - name: Install dependencies run: | - sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION ninja-build gcovr lcov liblapack-dev + sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION ninja-build gcovr lcov python -m pip install scipy - name: Build and run unit tests @@ -503,7 +503,7 @@ jobs: - name: Install dependencies run: | - sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION liblapack-dev + sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION python -m pip install scipy - name: Get required Python packages @@ -675,7 +675,7 @@ jobs: - name: Install dependencies run: | - sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION ninja-build gcovr lcov liblapack-dev + sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION ninja-build gcovr lcov python -m pip install scipy - name: Build and run unit tests From 97830a3933c13511a8f827d150a09538e89dd738 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Wed, 27 Mar 2024 13:03:23 -0400 Subject: [PATCH 403/428] remove lapack from gpu tests --- .github/workflows/tests_gpu_kokkos.yml | 6 +++--- .github/workflows/tests_linux_x86_mpi_gpu.yml | 17 +++-------------- 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/.github/workflows/tests_gpu_kokkos.yml b/.github/workflows/tests_gpu_kokkos.yml index 90b50dfece..e27773bcaa 100644 --- a/.github/workflows/tests_gpu_kokkos.yml +++ b/.github/workflows/tests_gpu_kokkos.yml @@ -80,7 +80,7 @@ jobs: CMAKE_PREFIX_PATH: ${{ github.workspace }}/Kokkos_install/${{ matrix.exec_model }} run: | set -x - sudo apt-get update && sudo apt-get -y -q install gfortran-$GCC_VERSION liblapack-dev + sudo apt-get update && sudo apt-get -y -q install gfortran-$GCC_VERSION cd kokkos_${{ matrix.exec_model }}/example/build_cmake_installed which -a gfortran || which -a gfortran-$GCC_VERSION cmake -B build -DCMAKE_Fortran_COMPILER=gfortran-$GCC_VERSION @@ -148,7 +148,7 @@ jobs: - name: Install dependencies run: | - sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION ninja-build gcovr lcov liblapack-dev + sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION ninja-build gcovr lcov - name: Build and run unit tests run: | @@ -252,7 +252,7 @@ jobs: python-version: '3.9' - name: Install dependencies - run: sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION liblapack-dev + run: sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION - name: Get required Python packages run: | diff --git a/.github/workflows/tests_linux_x86_mpi_gpu.yml b/.github/workflows/tests_linux_x86_mpi_gpu.yml index 7645a70ded..fffea88252 100644 --- a/.github/workflows/tests_linux_x86_mpi_gpu.yml +++ b/.github/workflows/tests_linux_x86_mpi_gpu.yml @@ -54,11 +54,6 @@ jobs: run: | git fetch tags --force git checkout $(git tag | sort -V | tail -1) - - uses: actions/setup-python@v4 - id: setup_python - name: Install Python - with: - python-version: '3.9' # Since the self-hosted runner can be re-used. It is best to set up all package # installations in a virtual environment that gets cleaned at the end of each workflow run @@ -96,8 +91,7 @@ jobs: python -m pip install -r requirements-dev.txt # Omitting the installation of cmake v3.29.0 due to # https://github.com/scikit-build/cmake-python-distributions/pull/474 - python -m pip install "cmake!=3.29.0" custatevec-cu12 - sudo apt-get -y -q install liblapack-dev + python -m pip install "cmake!=3.29.0" custatevec-cu12 scipy - name: Validate GPU version and installed compiler and modules run: | @@ -198,13 +192,6 @@ jobs: git fetch --tags --force git checkout $(git tag | sort -V | tail -1) - - - uses: actions/setup-python@v4 - id: setup_python - name: Install Python - with: - python-version: '3.9' - # Since the self-hosted runner can be re-used. It is best to set up all package # installations in a virtual environment that gets cleaned at the end of each workflow run - name: Setup Python virtual environment @@ -226,6 +213,8 @@ jobs: source ${{ env.VENV_NAME }}/bin/activate echo "venv_name=${{ env.VENV_NAME }}" >> $GITHUB_OUTPUT + python -m pip install scipy + - name: Display Python-Path id: python_path run: | From a0db3f0d653e060d3221ed5292207744aac0093e Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Wed, 27 Mar 2024 14:22:30 -0400 Subject: [PATCH 404/428] parse string to get scipy.libs in site-pack --- pennylane_lightning/core/src/utils/UtilLinearAlg.hpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index ac97da7afd..d745b7a23b 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -122,7 +122,14 @@ void compute_diagonalizing_gates(int n, int lda, if (!std::filesystem::exists(scipyPathStr)) { std::string currentPathStr(getPath()); - scipyPathStr = currentPathStr + "/../../scipy.libs"; + std::string site_packages_str("site-packages/"); + + std::size_t str_pos = currentPathStr.find(site_packages_str); + if (str_pos != std::string::npos) { + scipyPathStr = + currentPathStr.substr(0, str_pos + site_packages_str.size()); + scipyPathStr += "scipy.libs"; + } try { // convert the relative path to absolute path From cd8fba283a2d432ec19f2c2739a1bf498cf181df Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Wed, 27 Mar 2024 14:47:02 -0400 Subject: [PATCH 405/428] use scipy.libs only for lapack apis --- .../core/src/utils/UtilLinearAlg.hpp | 59 +++++++++---------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index d745b7a23b..fc54f9f9eb 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -58,6 +58,8 @@ std::unordered_map priority_lib = { namespace Pennylane::Util { +// Exclusively for python calls and tested in the python layer +// LCOV_EXCL_START #ifdef __linux__ /** * @brief Get the path to the current shared library object. @@ -80,6 +82,7 @@ inline const char *getPath() { // return fullPath.substr(0, pos); // } #endif +// LCOV_EXCL_STOP /** * @brief Decompose Hermitian matrix into diagonal matrix and unitaries @@ -117,9 +120,11 @@ void compute_diagonalizing_gates(int n, int lda, #else std::shared_ptr blasLib; std::vector> blasLibs; - + // For C++ usage std::string scipyPathStr(SCIPY_LIBS_PATH); + // Exclusively for python calls + // LCOV_EXCL_START if (!std::filesystem::exists(scipyPathStr)) { std::string currentPathStr(getPath()); std::string site_packages_str("site-packages/"); @@ -140,41 +145,35 @@ void compute_diagonalizing_gates(int n, int lda, << err.what() << '\n'; } } - if (!std::filesystem::exists(scipyPathStr)) { -#ifdef __linux__ - blasLib = std::make_shared("lapack.so"); -#endif - } else { - std::filesystem::path scipyLibsPath(scipyPathStr); - - std::vector> availableLibs; - - for (const auto &lib : - std::filesystem::directory_iterator(scipyLibsPath)) { - if (lib.is_regular_file()) { - for (const auto &iter : priority_lib) { - std::string libname_str = lib.path().filename().string(); - if (libname_str.find(iter.first) != std::string ::npos) { - availableLibs.emplace_back(libname_str, iter.second); - } + // LCOV_EXCL_STOP + + std::filesystem::path scipyLibsPath(scipyPathStr); + + std::vector> availableLibs; + + for (const auto &lib : std::filesystem::directory_iterator(scipyLibsPath)) { + if (lib.is_regular_file()) { + for (const auto &iter : priority_lib) { + std::string libname_str = lib.path().filename().string(); + if (libname_str.find(iter.first) != std::string ::npos) { + availableLibs.emplace_back(libname_str, iter.second); } } } + } - std::sort(availableLibs.begin(), availableLibs.end(), - [](const auto &lhs, const auto &rhs) { - return lhs.second < rhs.second; - }); - - for (const auto &lib : availableLibs) { - auto libPath = scipyLibsPath / lib.first.c_str(); - const std::string libPathStr = libPath.string(); - blasLibs.emplace_back( - std::make_shared(libPathStr)); - } + std::sort(availableLibs.begin(), availableLibs.end(), + [](const auto &lhs, const auto &rhs) { + return lhs.second < rhs.second; + }); - blasLib = blasLibs.back(); + for (const auto &lib : availableLibs) { + auto libPath = scipyLibsPath / lib.first.c_str(); + const std::string libPathStr = libPath.string(); + blasLibs.emplace_back(std::make_shared(libPathStr)); } + + blasLib = blasLibs.back(); #endif char jobz = 'V'; // Enable both eigenvalues and eigenvectors computation From f329ac0f0b837ebad5874fc12c25a9fde54d99e1 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Wed, 27 Mar 2024 14:54:04 -0400 Subject: [PATCH 406/428] tidy up code --- .github/workflows/tests_gpu_kokkos.yml | 5 ++--- .github/workflows/tests_linux.yml | 8 +------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/.github/workflows/tests_gpu_kokkos.yml b/.github/workflows/tests_gpu_kokkos.yml index e27773bcaa..c31488ad40 100644 --- a/.github/workflows/tests_gpu_kokkos.yml +++ b/.github/workflows/tests_gpu_kokkos.yml @@ -147,8 +147,7 @@ jobs: python-version: '3.9' - name: Install dependencies - run: | - sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION ninja-build gcovr lcov + run: sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION ninja-build gcovr lcov - name: Build and run unit tests run: | @@ -252,7 +251,7 @@ jobs: python-version: '3.9' - name: Install dependencies - run: sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION + run: sudo apt-get update && sudo apt-get -y -q install cmake gcc-$GCC_VERSION g++-$GCC_VERSION - name: Get required Python packages run: | diff --git a/.github/workflows/tests_linux.yml b/.github/workflows/tests_linux.yml index cf8d8ffc7e..37aac53254 100644 --- a/.github/workflows/tests_linux.yml +++ b/.github/workflows/tests_linux.yml @@ -42,7 +42,7 @@ jobs: name: Install Python with: python-version: '3.9' - + - name: Checkout PennyLane-Lightning uses: actions/checkout@v3 @@ -53,7 +53,6 @@ jobs: - name: Build and run unit tests run: | - set -x cmake . -BBuild -G Ninja \ -DCMAKE_BUILD_TYPE=Debug \ -DBUILD_TESTS=ON \ @@ -187,14 +186,12 @@ jobs: - name: Run PennyLane-Lightning unit tests run: | cd main/ - set -x DEVICENAME=`echo ${{ matrix.pl_backend }} | sed "s/_/./g"` OMP_NUM_THREADS=1 PL_DEVICE=${DEVICENAME} python -m pytest -n auto tests/ -k "not unitary_correct" $COVERAGE_FLAGS PL_DEVICE=${DEVICENAME} python -m pytest tests/ -k "unitary_correct" $COVERAGE_FLAGS --cov-append pl-device-test --device ${DEVICENAME} --skip-ops --shots=20000 $COVERAGE_FLAGS --cov-append pl-device-test --device ${DEVICENAME} --shots=None --skip-ops $COVERAGE_FLAGS --cov-append mv .coverage .coverage-${{ github.job }}-${{ matrix.pl_backend }} - set +x - name: Upload code coverage results uses: actions/upload-artifact@v3 @@ -230,7 +227,6 @@ jobs: - name: Build and run unit tests run: | - set -x cmake . -BBuild -G Ninja \ -DCMAKE_BUILD_TYPE=Debug \ -DENABLE_PYTHON=OFF \ @@ -262,7 +258,6 @@ jobs: path: ./Build/coverage-${{ github.job }}-${{ matrix.pl_backend }}.info if-no-files-found: error - pythontestswithOpenBLAS: strategy: matrix: @@ -411,7 +406,6 @@ jobs: - name: Build and run unit tests run: | - set -x cmake . -BBuild -G Ninja \ -DCMAKE_BUILD_TYPE=Debug \ -DBUILD_TESTS=ON \ From 9b2f8a8d4bf83e837b840a2527f9b6402b8e3328 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Wed, 27 Mar 2024 15:08:04 -0400 Subject: [PATCH 407/428] update docs and add changelog --- .github/CHANGELOG.md | 8 ++++++- README.rst | 21 ------------------- .../core/src/utils/UtilLinearAlg.hpp | 3 ++- 3 files changed, 9 insertions(+), 23 deletions(-) diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index f2b3950bfb..1022423a49 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -2,6 +2,9 @@ ### New features since last release +* Add dynamic linking to LAPACK/OpenBlas shared objects in scipy.libs for both C++ and Python layer. + [(#651)](https://github.com/PennyLaneAI/pennylane-lightning/pull/651) + * `lightning.qubit` supports mid-circuit measurements. [(#650)](https://github.com/PennyLaneAI/pennylane-lightning/pull/650) @@ -29,6 +32,9 @@ ### Breaking changes +* Deprecate static LAPACK linking support. + [(#651)](https://github.com/PennyLaneAI/pennylane-lightning/pull/651) + * Migrate `lightning.qubit` to the new device API. [(#646)](https://github.com/PennyLaneAI/pennylane-lightning/pull/646) @@ -60,7 +66,7 @@ This release contains contributions from (in alphabetical order): -Ali Asadi, Amintor Dusko, Christina Lee, Vincent Michaud-Rioux, Mudit Pandey +Ali Asadi, Amintor Dusko, Christina Lee, Vincent Michaud-Rioux, Mudit Pandey, Shuli Shu --- diff --git a/README.rst b/README.rst index 74090c8266..63980f2b8f 100644 --- a/README.rst +++ b/README.rst @@ -389,26 +389,6 @@ where ``${TARGET}`` is one of the following .. docker-end-inclusion-marker-do-not-remove -LAPACK support -************** - -LAPACK, a numerical linear algebra library, is required to enable stochastic measurement support in the C++ backend. We suggest first installing ``LAPACK`` following instructions in `LAPACK document `_. - -On Debian-based Linux systems, LAPACK can be also installed via ``apt``: - -.. code-block:: console - - $ sudo apt -y update && - $ sudo apt install liblapack-dev - -where LAPACK is included in ``liblapack-dev``. - -On Windows systems, LAPACK is recommended to be built and installed using `vcpkg `_ following the instructions `here `_. - -The CMake option ``-DENABLE_LAPACK=ON`` must also be specified when building C++ backends. For Windows systems, we suggest adding the CMake option ``-DCMAKE_TOOLCHAIN_FILE=/scripts/buildsystems/vcpkg.cmake`` to ensure ``LAPACK`` can be found with CMake. - -On MacOS systems, the Accelerate framework already includes optimized implementations of the LAPACK library. - Contributing ************ @@ -475,6 +455,5 @@ PennyLane Lightning makes use of the following libraries and tools, which are un - **pybind11:** https://github.com/pybind/pybind11 - **Kokkos Core:** https://github.com/kokkos/kokkos - **NVIDIA cuQuantum:** https://developer.nvidia.com/cuquantum-sdk -- **LAPACK:** https://github.com/Reference-LAPACK/lapack .. acknowledgements-end-inclusion-marker-do-not-remove diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index fc54f9f9eb..9e1786a839 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -43,7 +43,7 @@ /// @cond DEV namespace { -// LAPACK routine for complex Hermitian eigensystems +// Declare heev function pointers to access corresponding functions in LAPACK/OpenBlas using zheevPtr = void (*)(const char *, const char *, const int *, std::complex *, const int *, double *, std::complex *, const int *, double *, int *); @@ -51,6 +51,7 @@ using cheevPtr = void (*)(const char *, const char *, const int *, std::complex *, const int *, float *, std::complex *, const int *, float *, int *); +//Priority table used to load openblas and its dependencies in a correct order std::unordered_map priority_lib = { {"stdc", 0}, {"gcc", 1}, {"quadmath", 2}, {"gfortran", 3}, {"openblas", 4}}; } // namespace From 6ff1881e63610c320824e26c52b5536d7dfffb57 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Wed, 27 Mar 2024 15:24:34 -0400 Subject: [PATCH 408/428] make format --- pennylane_lightning/core/src/utils/UtilLinearAlg.hpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index 9e1786a839..adda736abe 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -43,7 +43,8 @@ /// @cond DEV namespace { -// Declare heev function pointers to access corresponding functions in LAPACK/OpenBlas +// Declare heev function pointers to access corresponding functions in +// LAPACK/OpenBLAS using zheevPtr = void (*)(const char *, const char *, const int *, std::complex *, const int *, double *, std::complex *, const int *, double *, int *); @@ -51,7 +52,7 @@ using cheevPtr = void (*)(const char *, const char *, const int *, std::complex *, const int *, float *, std::complex *, const int *, float *, int *); -//Priority table used to load openblas and its dependencies in a correct order +// Priority table used to sort openblas and its dependencies std::unordered_map priority_lib = { {"stdc", 0}, {"gcc", 1}, {"quadmath", 2}, {"gfortran", 3}, {"openblas", 4}}; } // namespace From 5bcae015e819be9f0a07137882c5d46f3bd76cbc Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Wed, 27 Mar 2024 15:34:12 -0400 Subject: [PATCH 409/428] tidy up code --- pennylane_lightning/core/src/utils/tests/CMakeLists.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pennylane_lightning/core/src/utils/tests/CMakeLists.txt b/pennylane_lightning/core/src/utils/tests/CMakeLists.txt index 4b9d5e01b9..5924cceb0e 100644 --- a/pennylane_lightning/core/src/utils/tests/CMakeLists.txt +++ b/pennylane_lightning/core/src/utils/tests/CMakeLists.txt @@ -32,9 +32,12 @@ set(TEST_SOURCES Test_BitUtil.cpp Test_RuntimeInfo.cpp Test_TypeTraits.cpp Test_Util.cpp - Test_UtilLinearAlg.cpp ) +if(NOT WIN32) + list(APPEND TEST_SOURCES Test_UtilLinearAlg.cpp) +endif() + add_executable(utils_test_runner ${TEST_SOURCES}) target_link_libraries(utils_test_runner PRIVATE utils_tests) From cdf75f16c1659dbedb5c4893a4074603181ac727 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Thu, 28 Mar 2024 16:24:10 -0400 Subject: [PATCH 410/428] init commit --- .github/workflows/wheel_win_x86_64.yml | 5 ++- CMakeLists.txt | 8 ++-- .../tests/Test_MeasurementsBase.cpp | 20 ++++----- .../core/src/observables/Observables.hpp | 23 +++++----- .../observables/tests/Test_Observables.cpp | 45 ++++++++++--------- .../core/src/utils/SharedLibLoader.hpp | 24 +++++----- .../core/src/utils/UtilLinearAlg.hpp | 20 ++++----- .../core/src/utils/tests/CMakeLists.txt | 7 +-- 8 files changed, 78 insertions(+), 74 deletions(-) diff --git a/.github/workflows/wheel_win_x86_64.yml b/.github/workflows/wheel_win_x86_64.yml index 530b6c6fc9..de7c627d1c 100644 --- a/.github/workflows/wheel_win_x86_64.yml +++ b/.github/workflows/wheel_win_x86_64.yml @@ -127,7 +127,8 @@ jobs: # Python build settings CIBW_BEFORE_BUILD: | - python -m pip install pybind11 cmake~=3.24.0 build + python -m pip install pybind11 cmake~=3.24.0 scipy + python -m pip install -r requirements.txt #Temporarily commenting while solving problems to find binaries in CIBW tests. # CIBW_BEFORE_TEST: | @@ -140,7 +141,7 @@ jobs: CIBW_BUILD_VERBOSITY: 3 - CIBW_BUILD_FRONTEND: build + #CIBW_BUILD_FRONTEND: build run: python -m cibuildwheel --output-dir wheelhouse diff --git a/CMakeLists.txt b/CMakeLists.txt index f375de584f..b9189a013f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -89,10 +89,10 @@ if (CMAKE_SYSTEM_NAME MATCHES "Windows" OR CMAKE_SYSTEM_NAME MATCHES "Linux") set(SCIPYLIBS ${Python_SITELIB}/scipy.libs) #TODO add windows support - #if (CMAKE_SYSTEM_NAME MATCHES "Windows") - # string(REPLACE "\\" "/" SCIPYLIBS "${SCIPYLIBS}") - # message("Python site-packages directory (Windows): ${SCIPYLIBS}") - #endif() + if (CMAKE_SYSTEM_NAME MATCHES "Windows") + string(REPLACE "\\" "/" SCIPYLIBS "${SCIPYLIBS}") + message("Python site-packages directory (Windows): ${SCIPYLIBS}") + endif() if(EXISTS ${SCIPYLIBS}) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h) diff --git a/pennylane_lightning/core/src/measurements/tests/Test_MeasurementsBase.cpp b/pennylane_lightning/core/src/measurements/tests/Test_MeasurementsBase.cpp index 7c3fe1d015..fa1a9bcc14 100644 --- a/pennylane_lightning/core/src/measurements/tests/Test_MeasurementsBase.cpp +++ b/pennylane_lightning/core/src/measurements/tests/Test_MeasurementsBase.cpp @@ -570,7 +570,7 @@ TEST_CASE("Expval Shot- NamedObs", "[MeasurementsBase][Observables]") { } } -#ifndef _MSC_VER +// #ifndef _MSC_VER template void testHermitianObsExpvalShot() { if constexpr (!std::is_same_v) { using StateVectorT = typename TypeList::Type; @@ -673,7 +673,7 @@ TEST_CASE("Expval Shot - HermitianObs ", "[MeasurementsBase][Observables]") { testHermitianObsExpvalShot(); } } -#endif +// #endif template void testHermitianObsExpval() { if constexpr (!std::is_same_v) { @@ -839,7 +839,7 @@ template void testTensorProdObsExpvalShot() { expected, static_cast(0.20))); } -#ifndef _MSC_VER + // #ifndef _MSC_VER DYNAMIC_SECTION(" With Identity and shots_range" << StateVectorToName::name) { size_t num_shots = 80000; @@ -860,7 +860,7 @@ template void testTensorProdObsExpvalShot() { REQUIRE_THAT(result, Catch::Matchers::WithinRel( expected, static_cast(0.20))); } -#endif + // #endif testTensorProdObsExpvalShot(); } @@ -1011,7 +1011,7 @@ TEST_CASE("Var - HermitianObs", "[MeasurementsBase][Observables]") { } } -#ifndef _MSC_VER +// #ifndef _MSC_VER template void testHermitianObsShotVar() { if constexpr (!std::is_same_v) { using StateVectorT = typename TypeList::Type; @@ -1096,7 +1096,7 @@ TEST_CASE("Var - HermitianObs Shot", "[MeasurementsBase][Observables]") { testHermitianObsShotVar(); } } -#endif +// #endif template void testTensorProdObsVarShot() { if constexpr (!std::is_same_v) { @@ -1147,7 +1147,7 @@ template void testTensorProdObsVarShot() { expected, static_cast(0.20))); } -#ifndef _MSC_VER + // #ifndef _MSC_VER DYNAMIC_SECTION("With Hermitian and NameObs" << StateVectorToName::name) { using MatrixT = std::vector; @@ -1197,7 +1197,7 @@ template void testTensorProdObsVarShot() { REQUIRE_THAT(result, Catch::Matchers::WithinRel( expected, static_cast(0.20))); } -#endif + // #endif DYNAMIC_SECTION(" full wires with apply operations" << StateVectorToName::name) { @@ -1555,7 +1555,7 @@ template void testHamiltonianObsExpvalShot() { REQUIRE_THAT(res, Catch::Matchers::WithinRel( expected, static_cast(0.20))); } -#ifndef _MSC_VER + // #ifndef _MSC_VER DYNAMIC_SECTION("YHer" << StateVectorToName::name) { auto Y0 = std::make_shared>( "PauliY", std::vector{0}); @@ -1576,7 +1576,7 @@ template void testHamiltonianObsExpvalShot() { REQUIRE_THAT(res, Catch::Matchers::WithinRel( expected, static_cast(0.20))); } -#endif + // #endif testHamiltonianObsExpvalShot(); } diff --git a/pennylane_lightning/core/src/observables/Observables.hpp b/pennylane_lightning/core/src/observables/Observables.hpp index 2a4697d937..41ef728b76 100644 --- a/pennylane_lightning/core/src/observables/Observables.hpp +++ b/pennylane_lightning/core/src/observables/Observables.hpp @@ -23,9 +23,9 @@ #include "Error.hpp" #include "Util.hpp" -#ifndef _MSC_VER +// #ifndef _MSC_VER #include "UtilLinearAlg.hpp" -#endif +// #endif namespace Pennylane::Observables { /** @@ -220,10 +220,10 @@ class HermitianObsBase : public Observable { MatrixT matrix_; std::vector wires_; -#ifndef _MSC_VER + // #ifndef _MSC_VER std::vector eigenVals_; MatrixT unitary_; -#endif + // #endif private: [[nodiscard]] auto isEqual(const Observable &other) const @@ -245,7 +245,7 @@ class HermitianObsBase : public Observable { : matrix_{std::move(matrix)}, wires_{std::move(wires)} { PL_ASSERT(matrix_.size() == Util::exp2(2 * wires_.size())); -#ifndef _MSC_VER + // #ifndef _MSC_VER std::vector> mat(matrix_.size()); std::transform(matrix_.begin(), matrix_.end(), mat.begin(), @@ -263,7 +263,7 @@ class HermitianObsBase : public Observable { std::transform( unitary.begin(), unitary.end(), unitary_.begin(), [](ComplexT value) { return static_cast(value); }); -#endif + // #endif } [[nodiscard]] auto getMatrix() const -> const MatrixT & { return matrix_; } @@ -284,7 +284,7 @@ class HermitianObsBase : public Observable { [[maybe_unused]] StateVectorT &sv, [[maybe_unused]] std::vector> &eigenValues, [[maybe_unused]] std::vector &ob_wires) const override { -#ifndef _MSC_VER + // #ifndef _MSC_VER std::vector> mat(matrix_.size()); std::transform(matrix_.begin(), matrix_.end(), mat.begin(), @@ -302,10 +302,11 @@ class HermitianObsBase : public Observable { ob_wires = wires_; sv.applyMatrix(unitary_, wires_); eigenValues.push_back(eigenVals_); -#else - PL_ABORT("Hermitian observables do not support shot measurement for " - "Windows."); -#endif + // #else + // PL_ABORT("Hermitian observables do not support shot + // measurement for " + // "Windows."); + // #endif } }; diff --git a/pennylane_lightning/core/src/observables/tests/Test_Observables.cpp b/pennylane_lightning/core/src/observables/tests/Test_Observables.cpp index f90c17eff0..099efb754f 100644 --- a/pennylane_lightning/core/src/observables/tests/Test_Observables.cpp +++ b/pennylane_lightning/core/src/observables/tests/Test_Observables.cpp @@ -207,27 +207,28 @@ template void testHermitianObsBase() { REQUIRE(ob2 != ob3); } -#ifndef _MSC_VER - DYNAMIC_SECTION("Failed to create a HermitianObs- " - << StateVectorToName::name) { - std::mt19937_64 re{1337}; - constexpr size_t num_qubits = 3; - auto init_state = - createRandomStateVectorData(re, num_qubits); - - StateVectorT state_vector(init_state.data(), init_state.size()); - auto obs = - HermitianObsT{std::vector{1.0, 0.0, 1.0, 0.0}, {0}}; - - std::vector> eigenValues; - std::vector ob_wires; - - REQUIRE_THROWS_WITH( - obs.applyInPlaceShots(state_vector, eigenValues, ob_wires), - Catch::Matchers::Contains("The matrix passed to HermitianObs " - "is not a Hermitian matrix.")); - } -#else + /*#ifndef _MSC_VER + DYNAMIC_SECTION("Failed to create a HermitianObs- " + << StateVectorToName::name) { + std::mt19937_64 re{1337}; + constexpr size_t num_qubits = 3; + auto init_state = + createRandomStateVectorData(re, num_qubits); + + StateVectorT state_vector(init_state.data(), + init_state.size()); auto obs = HermitianObsT{std::vector{1.0, + 0.0, 1.0, 0.0}, {0}}; + + std::vector> eigenValues; + std::vector ob_wires; + + REQUIRE_THROWS_WITH( + obs.applyInPlaceShots(state_vector, eigenValues, + ob_wires), Catch::Matchers::Contains("The matrix passed to HermitianObs + " "is not a Hermitian matrix.")); + } + #else + */ DYNAMIC_SECTION("Failed for HermitianObs for applyInPlaceShots - " << StateVectorToName::name) { std::mt19937_64 re{1337}; @@ -248,7 +249,7 @@ template void testHermitianObsBase() { "Hermitian observables do not support shot measurement for " "Windows.")); } -#endif + // #endif testHermitianObsBase(); } diff --git a/pennylane_lightning/core/src/utils/SharedLibLoader.hpp b/pennylane_lightning/core/src/utils/SharedLibLoader.hpp index cab9d51533..f3d7cdcbeb 100644 --- a/pennylane_lightning/core/src/utils/SharedLibLoader.hpp +++ b/pennylane_lightning/core/src/utils/SharedLibLoader.hpp @@ -22,8 +22,8 @@ #if defined(__APPLE__) || defined(__linux__) #include // TODO add windows support -// #elif defined(_MSC_VER) -// #include +#elif defined(_MSC_VER) +#include #endif #include "Error.hpp" @@ -42,8 +42,8 @@ class SharedLibLoader final { #if defined(__APPLE__) || defined(__linux__) void *handle_{nullptr}; // TODO add windows support -// #elif defined(_MSC_VER) -// HMODULE handle_{nullptr}; +#elif defined(_MSC_VER) + HMODULE handle_{nullptr}; #endif public: @@ -60,9 +60,9 @@ class SharedLibLoader final { // This allows users to use pre-installed LAPACK package PL_ABORT_IF(!handle_, dlerror()); // TODO add windows support -// #elif defined(_MSC_VER) -// handle_ = LoadLibrary(filename.c_str()); -// PL_ABORT_IF(!handle_, std::to_string(GetLastError())); +#elif defined(_MSC_VER) + handle_ = LoadLibrary(filename.c_str()); + PL_ABORT_IF(!handle_, std::to_string(GetLastError())); #endif } @@ -70,8 +70,8 @@ class SharedLibLoader final { #if defined(__APPLE__) || defined(__linux__) dlclose(handle_); // TODO add windows support -// #elif defined(_MSC_VER) -// FreeLibrary(handle_); +#elif defined(_MSC_VER) + FreeLibrary(handle_); #endif } @@ -82,9 +82,9 @@ class SharedLibLoader final { void *sym = dlsym(handle_, symbol.c_str()); PL_ABORT_IF(!sym, dlerror()); // TODO add windows support -// #elif defined(_MSC_VER) -// void *sym = GetProcAddress(handle_, symbol.c_str()); -// PL_ABORT_IF(!handle_, std::to_string(GetLastError())); +#elif defined(_MSC_VER) + void *sym = GetProcAddress(handle_, symbol.c_str()); + PL_ABORT_IF(!handle_, std::to_string(GetLastError())); #endif return sym; } diff --git a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp index adda736abe..bba5d7a430 100644 --- a/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp +++ b/pennylane_lightning/core/src/utils/UtilLinearAlg.hpp @@ -33,8 +33,8 @@ #if defined(__APPLE__) || defined(__linux__) #include // TODO add windows support -// #elif defined(_MSC_VER) -// #include +#elif defined(_MSC_VER) +#include #endif #include "SharedLibLoader.hpp" @@ -75,14 +75,14 @@ inline const char *getPath() { return dl_info.dli_fname; } // TODO add windows support -// #elif defined(_MSC_VER) -// inline std::string getPath() { -// char buffer[MAX_PATH]; -// GetModuleFileName(nullptr, buffer, MAX_PATH); -// std::string fullPath(buffer); -// std::size_t pos = fullPath.find_last_of("\\/"); -// return fullPath.substr(0, pos); -// } +#elif defined(_MSC_VER) +inline std::string getPath() { + char buffer[MAX_PATH]; + GetModuleFileName(nullptr, buffer, MAX_PATH); + std::string fullPath(buffer); + std::size_t pos = fullPath.find_last_of("\\/"); + return fullPath.substr(0, pos); +} #endif // LCOV_EXCL_STOP diff --git a/pennylane_lightning/core/src/utils/tests/CMakeLists.txt b/pennylane_lightning/core/src/utils/tests/CMakeLists.txt index 5924cceb0e..92ca95d66b 100644 --- a/pennylane_lightning/core/src/utils/tests/CMakeLists.txt +++ b/pennylane_lightning/core/src/utils/tests/CMakeLists.txt @@ -32,11 +32,12 @@ set(TEST_SOURCES Test_BitUtil.cpp Test_RuntimeInfo.cpp Test_TypeTraits.cpp Test_Util.cpp + Test_UtilLinearAlg.cpp ) -if(NOT WIN32) - list(APPEND TEST_SOURCES Test_UtilLinearAlg.cpp) -endif() +#if(NOT WIN32) +# list(APPEND TEST_SOURCES Test_UtilLinearAlg.cpp) +#endif() add_executable(utils_test_runner ${TEST_SOURCES}) target_link_libraries(utils_test_runner PRIVATE utils_tests) From 73e6c65c697446c07d0cde738d278305ff7ce792 Mon Sep 17 00:00:00 2001 From: Dev version update bot Date: Thu, 28 Mar 2024 20:33:47 +0000 Subject: [PATCH 411/428] Auto update version --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index 58da0aa862..19067082da 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.36.0-dev20" +__version__ = "0.36.0-dev21" From 32b4e197e69a4cfa754e7f76862756bbe55d7188 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Thu, 28 Mar 2024 17:08:45 -0400 Subject: [PATCH 412/428] quick test --- .github/workflows/tests_gpu_cuda.yml | 2 +- .github/workflows/tests_gpu_kokkos.yml | 2 +- .github/workflows/tests_linux.yml | 2 +- .github/workflows/tests_linux_x86_mpi_gpu.yml | 2 +- .github/workflows/tests_without_binary.yml | 2 +- setup.py | 1 + 6 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/tests_gpu_cuda.yml b/.github/workflows/tests_gpu_cuda.yml index b2ef63dbaf..1613b02fe9 100644 --- a/.github/workflows/tests_gpu_cuda.yml +++ b/.github/workflows/tests_gpu_cuda.yml @@ -11,7 +11,7 @@ on: required: true description: The version of PennyLane to use. Valid values are either 'release' (most recent release candidate), 'stable' (most recent git-tag) or 'latest' (most recent commit from master) release: - pull_request: + #pull_request: push: branches: - master diff --git a/.github/workflows/tests_gpu_kokkos.yml b/.github/workflows/tests_gpu_kokkos.yml index c31488ad40..9307781c3f 100644 --- a/.github/workflows/tests_gpu_kokkos.yml +++ b/.github/workflows/tests_gpu_kokkos.yml @@ -10,7 +10,7 @@ on: type: string required: true description: The version of PennyLane to use. Valid values are either 'release' (most recent release candidate), 'stable' (most recent git-tag) or 'latest' (most recent commit from master) - pull_request: + #pull_request: push: branches: - master diff --git a/.github/workflows/tests_linux.yml b/.github/workflows/tests_linux.yml index 37aac53254..23fe4e7e05 100644 --- a/.github/workflows/tests_linux.yml +++ b/.github/workflows/tests_linux.yml @@ -10,7 +10,7 @@ on: type: string required: true description: The version of PennyLane to use. Valid values are either 'release' (most recent release candidate), 'stable' (most recent git-tag) or 'latest' (most recent commit from master) - pull_request: + #pull_request: push: branches: - master diff --git a/.github/workflows/tests_linux_x86_mpi_gpu.yml b/.github/workflows/tests_linux_x86_mpi_gpu.yml index fffea88252..7d73a98b0b 100644 --- a/.github/workflows/tests_linux_x86_mpi_gpu.yml +++ b/.github/workflows/tests_linux_x86_mpi_gpu.yml @@ -14,7 +14,7 @@ on: push: branches: - main - pull_request: + #pull_request: env: COVERAGE_FLAGS: "--cov=pennylane_lightning --cov-report=term-missing --cov-report=xml:./coverage.xml --no-flaky-report -p no:warnings --tb=native" diff --git a/.github/workflows/tests_without_binary.yml b/.github/workflows/tests_without_binary.yml index e01964ce2b..9af0aa9707 100644 --- a/.github/workflows/tests_without_binary.yml +++ b/.github/workflows/tests_without_binary.yml @@ -10,7 +10,7 @@ on: type: string required: true description: The version of PennyLane to use. Valid values are either 'release' (most recent release candidate), 'stable' (most recent git-tag) or 'latest' (most recent commit from master) - pull_request: + #pull_request: push: branches: - master diff --git a/setup.py b/setup.py index 977f753559..21b2142240 100644 --- a/setup.py +++ b/setup.py @@ -171,6 +171,7 @@ def build_extension(self, ext: CMakeExtension): requirements = [ "pennylane>=0.34", + "scipy" ] packages_list = ["pennylane_lightning." + backend] From c7ec8ac3426b6f5504dbb3a6e5e47eb736c4493e Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Thu, 28 Mar 2024 19:52:13 -0400 Subject: [PATCH 413/428] quick test --- CMakeLists.txt | 27 +++++++++++++++++++++++++-- setup.py | 1 - 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b9189a013f..de547e37b0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -86,9 +86,30 @@ find_package(Python COMPONENTS Interpreter Development) message("Python site-packages directory: ${Python_SITELIB}") if (CMAKE_SYSTEM_NAME MATCHES "Windows" OR CMAKE_SYSTEM_NAME MATCHES "Linux") - set(SCIPYLIBS ${Python_SITELIB}/scipy.libs) + #set(SCIPYLIBS ${Python_SITELIB}/scipy.libs) + + ##TODO add windows support + #if (CMAKE_SYSTEM_NAME MATCHES "Windows") + # string(REPLACE "\\" "/" SCIPYLIBS "${SCIPYLIBS}") + # message("Python site-packages directory (Windows): ${SCIPYLIBS}") + #endif() + + execute_process( + COMMAND ${Python_EXECUTABLE} -m pip install scipy + RESULT_VARIABLE result + OUTPUT_VARIABLE pip_output + ) + + if(result) + message(FATAL_ERROR "Failed to install cmake package via pip") + else() + message(STATUS "+++++++++${pip_output}") + string(REGEX MATCH "/.* " extracted_path ${pip_output}) + string(REGEX REPLACE "packages.*" "packages" extracted_path ${extracted_path}) + message(STATUS "${extracted_path}") + set(SCIPYLIBS ${extracted_path}/scipy.libs) + endif() - #TODO add windows support if (CMAKE_SYSTEM_NAME MATCHES "Windows") string(REPLACE "\\" "/" SCIPYLIBS "${SCIPYLIBS}") message("Python site-packages directory (Windows): ${SCIPYLIBS}") @@ -96,6 +117,8 @@ if (CMAKE_SYSTEM_NAME MATCHES "Windows" OR CMAKE_SYSTEM_NAME MATCHES "Linux") if(EXISTS ${SCIPYLIBS}) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h) + elseif() + message(FATAL_ERROR "scipy.libs not found.") endif() else() set(SCIPYLIBS "/System/Library/Frameworks/Accelerate.framework/Versions/Current/Frameworks/vecLib.framework/libLAPACK.dylib") diff --git a/setup.py b/setup.py index 21b2142240..977f753559 100644 --- a/setup.py +++ b/setup.py @@ -171,7 +171,6 @@ def build_extension(self, ext: CMakeExtension): requirements = [ "pennylane>=0.34", - "scipy" ] packages_list = ["pennylane_lightning." + backend] From 1a7dfd18a01bfc18c84b59db14cb7cfa1ddcbac0 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Thu, 28 Mar 2024 20:03:10 -0400 Subject: [PATCH 414/428] install scipy if not found with python_sitelib --- CMakeLists.txt | 41 ++++++++++++++++++----------------------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index de547e37b0..bd223b3b88 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -86,33 +86,28 @@ find_package(Python COMPONENTS Interpreter Development) message("Python site-packages directory: ${Python_SITELIB}") if (CMAKE_SYSTEM_NAME MATCHES "Windows" OR CMAKE_SYSTEM_NAME MATCHES "Linux") - #set(SCIPYLIBS ${Python_SITELIB}/scipy.libs) + set(SCIPYLIBS ${Python_SITELIB}/scipy.libs) ##TODO add windows support - #if (CMAKE_SYSTEM_NAME MATCHES "Windows") - # string(REPLACE "\\" "/" SCIPYLIBS "${SCIPYLIBS}") - # message("Python site-packages directory (Windows): ${SCIPYLIBS}") - #endif() - - execute_process( - COMMAND ${Python_EXECUTABLE} -m pip install scipy - RESULT_VARIABLE result - OUTPUT_VARIABLE pip_output - ) - - if(result) - message(FATAL_ERROR "Failed to install cmake package via pip") - else() - message(STATUS "+++++++++${pip_output}") - string(REGEX MATCH "/.* " extracted_path ${pip_output}) - string(REGEX REPLACE "packages.*" "packages" extracted_path ${extracted_path}) - message(STATUS "${extracted_path}") - set(SCIPYLIBS ${extracted_path}/scipy.libs) - endif() - if (CMAKE_SYSTEM_NAME MATCHES "Windows") string(REPLACE "\\" "/" SCIPYLIBS "${SCIPYLIBS}") - message("Python site-packages directory (Windows): ${SCIPYLIBS}") + endif() + + if(NOT EXISTS ${SCIPYLIBS}) + execute_process( + COMMAND ${Python_EXECUTABLE} -m pip install scipy + RESULT_VARIABLE result + OUTPUT_VARIABLE pip_output + ) + + if(result) + message(FATAL_ERROR "Failed to install cmake package via pip") + else() + set(SCIPYLIBS ${Python_SITELIB}/scipy.libs) + if (CMAKE_SYSTEM_NAME MATCHES "Windows") + string(REPLACE "\\" "/" SCIPYLIBS "${SCIPYLIBS}") + endif() + endif() endif() if(EXISTS ${SCIPYLIBS}) From fd848070a5354a0f9326deb31c46a34211a4d985 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Thu, 28 Mar 2024 20:12:45 -0400 Subject: [PATCH 415/428] quick tests --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index bd223b3b88..f89341df1b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -108,6 +108,7 @@ if (CMAKE_SYSTEM_NAME MATCHES "Windows" OR CMAKE_SYSTEM_NAME MATCHES "Linux") string(REPLACE "\\" "/" SCIPYLIBS "${SCIPYLIBS}") endif() endif() + message(STATUS "+++++++++++${SCIPYLIBS}") endif() if(EXISTS ${SCIPYLIBS}) From 105f95e4d616095c825a6829fe1b4c0a2d31d360 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Thu, 28 Mar 2024 20:24:14 -0400 Subject: [PATCH 416/428] use IS_DIRECTORY to check --- .github/workflows/wheel_win_x86_64.yml | 2 +- CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/wheel_win_x86_64.yml b/.github/workflows/wheel_win_x86_64.yml index de7c627d1c..b827a2ab78 100644 --- a/.github/workflows/wheel_win_x86_64.yml +++ b/.github/workflows/wheel_win_x86_64.yml @@ -127,7 +127,7 @@ jobs: # Python build settings CIBW_BEFORE_BUILD: | - python -m pip install pybind11 cmake~=3.24.0 scipy + python -m pip install pybind11 cmake~=3.24.0 python -m pip install -r requirements.txt #Temporarily commenting while solving problems to find binaries in CIBW tests. diff --git a/CMakeLists.txt b/CMakeLists.txt index f89341df1b..6163bcd391 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -111,7 +111,7 @@ if (CMAKE_SYSTEM_NAME MATCHES "Windows" OR CMAKE_SYSTEM_NAME MATCHES "Linux") message(STATUS "+++++++++++${SCIPYLIBS}") endif() - if(EXISTS ${SCIPYLIBS}) + if(IS_DIRECTORY ${SCIPYLIBS}) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h) elseif() message(FATAL_ERROR "scipy.libs not found.") From d9aada98a474a498b1c00454e6479ae8d46348e4 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Thu, 28 Mar 2024 20:28:06 -0400 Subject: [PATCH 417/428] quick fix --- pennylane_lightning/core/src/utils/CMakeLists.txt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pennylane_lightning/core/src/utils/CMakeLists.txt b/pennylane_lightning/core/src/utils/CMakeLists.txt index 22a3811467..55d1095760 100644 --- a/pennylane_lightning/core/src/utils/CMakeLists.txt +++ b/pennylane_lightning/core/src/utils/CMakeLists.txt @@ -4,13 +4,10 @@ project(lightning_utils LANGUAGES CXX) set(LQUBIT_UTILS_FILES RuntimeInfo.cpp CACHE INTERNAL "" FORCE) +list(APPEND LQUBIT_UTILS_FILES UtilLinearAlg.cpp) add_library(lightning_utils STATIC ${LQUBIT_UTILS_FILES}) -if(NOT WIN32) - list(APPEND LQUBIT_UTILS_FILES UtilLinearAlg.cpp) -endif() - target_include_directories(lightning_utils INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(lightning_utils INTERFACE lightning_compile_options lightning_external_libs From 8a25628e91191731b462b646f4632eaeb8f2415f Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Thu, 28 Mar 2024 20:40:43 -0400 Subject: [PATCH 418/428] quick test --- CMakeLists.txt | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6163bcd391..b4cc7869d7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -93,23 +93,23 @@ if (CMAKE_SYSTEM_NAME MATCHES "Windows" OR CMAKE_SYSTEM_NAME MATCHES "Linux") string(REPLACE "\\" "/" SCIPYLIBS "${SCIPYLIBS}") endif() - if(NOT EXISTS ${SCIPYLIBS}) - execute_process( - COMMAND ${Python_EXECUTABLE} -m pip install scipy - RESULT_VARIABLE result - OUTPUT_VARIABLE pip_output - ) - - if(result) - message(FATAL_ERROR "Failed to install cmake package via pip") - else() - set(SCIPYLIBS ${Python_SITELIB}/scipy.libs) - if (CMAKE_SYSTEM_NAME MATCHES "Windows") - string(REPLACE "\\" "/" SCIPYLIBS "${SCIPYLIBS}") - endif() - endif() - message(STATUS "+++++++++++${SCIPYLIBS}") - endif() + #if(NOT EXISTS ${SCIPYLIBS}) + # execute_process( + # COMMAND ${Python_EXECUTABLE} -m pip install scipy + # RESULT_VARIABLE result + # OUTPUT_VARIABLE pip_output + # ) + + # if(result) + # message(FATAL_ERROR "Failed to install cmake package via pip") + # else() + # set(SCIPYLIBS ${Python_SITELIB}/scipy.libs) + # if (CMAKE_SYSTEM_NAME MATCHES "Windows") + # string(REPLACE "\\" "/" SCIPYLIBS "${SCIPYLIBS}") + # endif() + # endif() + # message(STATUS "+++++++++++${SCIPYLIBS}") + #endif() if(IS_DIRECTORY ${SCIPYLIBS}) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h) From 554a1d63d3e87670faf9364127d60d39c2fcacc1 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Thu, 28 Mar 2024 20:59:08 -0400 Subject: [PATCH 419/428] ensure scipy is installed when running setup.py --- CMakeLists.txt | 18 ------------------ setup.py | 13 +++++++++++++ 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b4cc7869d7..2bb6523c76 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -93,24 +93,6 @@ if (CMAKE_SYSTEM_NAME MATCHES "Windows" OR CMAKE_SYSTEM_NAME MATCHES "Linux") string(REPLACE "\\" "/" SCIPYLIBS "${SCIPYLIBS}") endif() - #if(NOT EXISTS ${SCIPYLIBS}) - # execute_process( - # COMMAND ${Python_EXECUTABLE} -m pip install scipy - # RESULT_VARIABLE result - # OUTPUT_VARIABLE pip_output - # ) - - # if(result) - # message(FATAL_ERROR "Failed to install cmake package via pip") - # else() - # set(SCIPYLIBS ${Python_SITELIB}/scipy.libs) - # if (CMAKE_SYSTEM_NAME MATCHES "Windows") - # string(REPLACE "\\" "/" SCIPYLIBS "${SCIPYLIBS}") - # endif() - # endif() - # message(STATUS "+++++++++++${SCIPYLIBS}") - #endif() - if(IS_DIRECTORY ${SCIPYLIBS}) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/pennylane_lightning/core/src/utils/config.h) elseif() diff --git a/setup.py b/setup.py index 977f753559..6d8df84fb1 100644 --- a/setup.py +++ b/setup.py @@ -16,6 +16,7 @@ import subprocess import shutil import sys +import pip from pathlib import Path from setuptools import setup, Extension, find_namespace_packages from setuptools.command.build_ext import build_ext @@ -58,6 +59,12 @@ def get_backend(): raise ValueError(f"Invalid backend {backend}.") return backend +def install_scipy(): + try: + pip.main(['install', 'scipy']) + print("scipy installed successfully.") + except Exception as e: + print("Error installing scipy:", str(e)) backend = get_backend() device_name = backend.replace("_", ".") @@ -76,6 +83,8 @@ class CMakeBuild(build_ext): user_options = build_ext.user_options + [("define=", "D", "Define variables for CMake")] + install_scipy() + def initialize_options(self): super().initialize_options() self.define = None @@ -109,6 +118,10 @@ def build_extension(self, ext: CMakeExtension): else [f"-DPython_EXECUTABLE={sys.executable}"] ) + configure_args += [f"-DPython_SITELIBS={site.getsitepackages()[0]}"] + + + if platform.system() == "Windows": # As Ninja does not support long path for windows yet: # (https://github.com/ninja-build/ninja/pull/2056) From da7cb6beca804a024e32b1c5a980ac3287086605 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Thu, 28 Mar 2024 21:03:03 -0400 Subject: [PATCH 420/428] bug fix --- setup.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/setup.py b/setup.py index 6d8df84fb1..e67e238ced 100644 --- a/setup.py +++ b/setup.py @@ -117,11 +117,7 @@ def build_extension(self, ext: CMakeExtension): if platform.system() == "Linux" else [f"-DPython_EXECUTABLE={sys.executable}"] ) - - configure_args += [f"-DPython_SITELIBS={site.getsitepackages()[0]}"] - - if platform.system() == "Windows": # As Ninja does not support long path for windows yet: # (https://github.com/ninja-build/ninja/pull/2056) From e5b59867e00357dad94f52d389b9aca01b80c097 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Fri, 29 Mar 2024 08:55:21 -0400 Subject: [PATCH 421/428] pass site_directory from setup.py --- CMakeLists.txt | 6 +++++- setup.py | 13 +++---------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2bb6523c76..098875e6bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -86,7 +86,11 @@ find_package(Python COMPONENTS Interpreter Development) message("Python site-packages directory: ${Python_SITELIB}") if (CMAKE_SYSTEM_NAME MATCHES "Windows" OR CMAKE_SYSTEM_NAME MATCHES "Linux") - set(SCIPYLIBS ${Python_SITELIB}/scipy.libs) + if(IS_DIRECTORY ${SCIPY_LIBS_DIR}) + set(SCIPYLIBS ${SCIPY_LIBS_DIR}/scipy.libs) + else() + set(SCIPYLIBS ${Python_SITELIB}/scipy.libs) + endif() ##TODO add windows support if (CMAKE_SYSTEM_NAME MATCHES "Windows") diff --git a/setup.py b/setup.py index e67e238ced..c322769fb4 100644 --- a/setup.py +++ b/setup.py @@ -16,7 +16,7 @@ import subprocess import shutil import sys -import pip +import site from pathlib import Path from setuptools import setup, Extension, find_namespace_packages from setuptools.command.build_ext import build_ext @@ -59,13 +59,6 @@ def get_backend(): raise ValueError(f"Invalid backend {backend}.") return backend -def install_scipy(): - try: - pip.main(['install', 'scipy']) - print("scipy installed successfully.") - except Exception as e: - print("Error installing scipy:", str(e)) - backend = get_backend() device_name = backend.replace("_", ".") @@ -83,8 +76,6 @@ class CMakeBuild(build_ext): user_options = build_ext.user_options + [("define=", "D", "Define variables for CMake")] - install_scipy() - def initialize_options(self): super().initialize_options() self.define = None @@ -117,6 +108,8 @@ def build_extension(self, ext: CMakeExtension): if platform.system() == "Linux" else [f"-DPython_EXECUTABLE={sys.executable}"] ) + + configure_args += [f"-DSCIPY_LIBS_DIR={site.getsitepackages()[0]}"] if platform.system() == "Windows": # As Ninja does not support long path for windows yet: From f18b0c6eb83795a1ee4ef8a37b7b6642d3ac6ccc Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Fri, 29 Mar 2024 09:19:56 -0400 Subject: [PATCH 422/428] quick test --- .../gates/OpToMemberFuncPtr.hpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/pennylane_lightning/core/src/simulators/lightning_qubit/gates/OpToMemberFuncPtr.hpp b/pennylane_lightning/core/src/simulators/lightning_qubit/gates/OpToMemberFuncPtr.hpp index 2875b22a6d..6f1705b318 100644 --- a/pennylane_lightning/core/src/simulators/lightning_qubit/gates/OpToMemberFuncPtr.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_qubit/gates/OpToMemberFuncPtr.hpp @@ -42,7 +42,7 @@ template struct GateOpToMemberFuncPtr { // raises compile error when this struct is instantiated. - static_assert(sizeof(PrecisionT) == std::numeric_limits::max(), + PL_ABORT_IF_NOT(sizeof(PrecisionT) == std::numeric_limits::max(), "GateOpToMemberFuncPtr is not defined for the given gate. " "When you define a new GateOperation, check that you also " "have added the corresponding entry in " @@ -269,7 +269,7 @@ struct GateOpToMemberFuncPtr struct ControlledGateOpToMemberFuncPtr { - static_assert(sizeof(PrecisionT) == std::numeric_limits::max(), + PL_ABORT_IF_NOT(sizeof(PrecisionT) == std::numeric_limits::max(), "Unrecognized matrix operation"); }; template @@ -437,7 +437,7 @@ template struct GeneratorOpToMemberFuncPtr { // raises compile error when this struct is instantiated. - static_assert( + PL_ABORT_IF_NOT( sizeof(GateImplementation) == std::numeric_limits::max(), "GeneratorOpToMemberFuncPtr is not defined for the given generator. " "When you define a new GeneratorOperation, check that you also " @@ -575,7 +575,7 @@ struct GeneratorOpToMemberFuncPtr struct ControlledGeneratorOpToMemberFuncPtr { - static_assert(sizeof(PrecisionT) == std::numeric_limits::max(), + PL_ABORT_IF_NOT(sizeof(PrecisionT) == std::numeric_limits::max(), "Unrecognized generator operation"); }; template @@ -692,7 +692,7 @@ struct ControlledGeneratorOpToMemberFuncPtr< */ template struct MatrixOpToMemberFuncPtr { - static_assert(sizeof(PrecisionT) == std::numeric_limits::max(), + PL_ABORT_IF_NOT(sizeof(PrecisionT) == std::numeric_limits::max(), "Unrecognized matrix operation"); }; @@ -718,7 +718,7 @@ struct MatrixOpToMemberFuncPtr struct ControlledMatrixOpToMemberFuncPtr { - static_assert(sizeof(PrecisionT) == std::numeric_limits::max(), + PL_ABORT_IF_NOT(sizeof(PrecisionT) == std::numeric_limits::max(), "Unrecognized matrix operation"); }; template @@ -748,7 +748,7 @@ namespace Internal { * types. */ template struct GateMemFuncPtr { - static_assert(num_params < 2 || num_params == 3, + PL_ABORT_IF_NOT(num_params < 2 || num_params == 3, "The given num_params is not supported."); }; /** @@ -783,7 +783,7 @@ using GateMemFuncPtrT = */ template struct GateFuncPtr { - static_assert(num_params < 2 || num_params == 3, + PL_ABORT_IF_NOT(num_params < 2 || num_params == 3, "The given num_params is not supported."); }; @@ -818,7 +818,7 @@ struct GateFuncPtr { */ template struct ControlledGateFuncPtr { - static_assert(num_params < 2 || num_params == 3, + PL_ABORT_IF_NOT(num_params < 2 || num_params == 3, "The given num_params is not supported."); }; template From 354151fdb295c07bec49b3d5c12e905665ebf8c7 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Sun, 31 Mar 2024 11:22:14 -0400 Subject: [PATCH 423/428] revert to static_assert --- .../gates/OpToMemberFuncPtr.hpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/pennylane_lightning/core/src/simulators/lightning_qubit/gates/OpToMemberFuncPtr.hpp b/pennylane_lightning/core/src/simulators/lightning_qubit/gates/OpToMemberFuncPtr.hpp index 6f1705b318..2875b22a6d 100644 --- a/pennylane_lightning/core/src/simulators/lightning_qubit/gates/OpToMemberFuncPtr.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_qubit/gates/OpToMemberFuncPtr.hpp @@ -42,7 +42,7 @@ template struct GateOpToMemberFuncPtr { // raises compile error when this struct is instantiated. - PL_ABORT_IF_NOT(sizeof(PrecisionT) == std::numeric_limits::max(), + static_assert(sizeof(PrecisionT) == std::numeric_limits::max(), "GateOpToMemberFuncPtr is not defined for the given gate. " "When you define a new GateOperation, check that you also " "have added the corresponding entry in " @@ -269,7 +269,7 @@ struct GateOpToMemberFuncPtr struct ControlledGateOpToMemberFuncPtr { - PL_ABORT_IF_NOT(sizeof(PrecisionT) == std::numeric_limits::max(), + static_assert(sizeof(PrecisionT) == std::numeric_limits::max(), "Unrecognized matrix operation"); }; template @@ -437,7 +437,7 @@ template struct GeneratorOpToMemberFuncPtr { // raises compile error when this struct is instantiated. - PL_ABORT_IF_NOT( + static_assert( sizeof(GateImplementation) == std::numeric_limits::max(), "GeneratorOpToMemberFuncPtr is not defined for the given generator. " "When you define a new GeneratorOperation, check that you also " @@ -575,7 +575,7 @@ struct GeneratorOpToMemberFuncPtr struct ControlledGeneratorOpToMemberFuncPtr { - PL_ABORT_IF_NOT(sizeof(PrecisionT) == std::numeric_limits::max(), + static_assert(sizeof(PrecisionT) == std::numeric_limits::max(), "Unrecognized generator operation"); }; template @@ -692,7 +692,7 @@ struct ControlledGeneratorOpToMemberFuncPtr< */ template struct MatrixOpToMemberFuncPtr { - PL_ABORT_IF_NOT(sizeof(PrecisionT) == std::numeric_limits::max(), + static_assert(sizeof(PrecisionT) == std::numeric_limits::max(), "Unrecognized matrix operation"); }; @@ -718,7 +718,7 @@ struct MatrixOpToMemberFuncPtr struct ControlledMatrixOpToMemberFuncPtr { - PL_ABORT_IF_NOT(sizeof(PrecisionT) == std::numeric_limits::max(), + static_assert(sizeof(PrecisionT) == std::numeric_limits::max(), "Unrecognized matrix operation"); }; template @@ -748,7 +748,7 @@ namespace Internal { * types. */ template struct GateMemFuncPtr { - PL_ABORT_IF_NOT(num_params < 2 || num_params == 3, + static_assert(num_params < 2 || num_params == 3, "The given num_params is not supported."); }; /** @@ -783,7 +783,7 @@ using GateMemFuncPtrT = */ template struct GateFuncPtr { - PL_ABORT_IF_NOT(num_params < 2 || num_params == 3, + static_assert(num_params < 2 || num_params == 3, "The given num_params is not supported."); }; @@ -818,7 +818,7 @@ struct GateFuncPtr { */ template struct ControlledGateFuncPtr { - PL_ABORT_IF_NOT(num_params < 2 || num_params == 3, + static_assert(num_params < 2 || num_params == 3, "The given num_params is not supported."); }; template From 610fe80f390ed89e10f7052cee5971bcef91452a Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Sun, 31 Mar 2024 11:42:58 -0400 Subject: [PATCH 424/428] add cstddef to OpToMemberFuncPtr --- .../src/simulators/lightning_qubit/gates/OpToMemberFuncPtr.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/pennylane_lightning/core/src/simulators/lightning_qubit/gates/OpToMemberFuncPtr.hpp b/pennylane_lightning/core/src/simulators/lightning_qubit/gates/OpToMemberFuncPtr.hpp index 2875b22a6d..6b36ccb4c4 100644 --- a/pennylane_lightning/core/src/simulators/lightning_qubit/gates/OpToMemberFuncPtr.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_qubit/gates/OpToMemberFuncPtr.hpp @@ -20,6 +20,7 @@ #pragma once #include +#include #include #include From aeb41f9f45e72d5f203669d4309ec50f41944b67 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Sun, 31 Mar 2024 11:55:17 -0400 Subject: [PATCH 425/428] type cast --- pennylane_lightning/core/src/utils/SharedLibLoader.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/src/utils/SharedLibLoader.hpp b/pennylane_lightning/core/src/utils/SharedLibLoader.hpp index f3d7cdcbeb..2086112f47 100644 --- a/pennylane_lightning/core/src/utils/SharedLibLoader.hpp +++ b/pennylane_lightning/core/src/utils/SharedLibLoader.hpp @@ -83,7 +83,7 @@ class SharedLibLoader final { PL_ABORT_IF(!sym, dlerror()); // TODO add windows support #elif defined(_MSC_VER) - void *sym = GetProcAddress(handle_, symbol.c_str()); + void *sym = reinterpret_cast(GetProcAddress(handle_, symbol.c_str())); PL_ABORT_IF(!handle_, std::to_string(GetLastError())); #endif return sym; From 763db6182d2efbb827660ca185f3f23195f5e1ec Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Sun, 31 Mar 2024 12:25:43 -0400 Subject: [PATCH 426/428] debug --- pennylane_lightning/core/src/utils/SharedLibLoader.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/src/utils/SharedLibLoader.hpp b/pennylane_lightning/core/src/utils/SharedLibLoader.hpp index 2086112f47..6ca9f7c3b4 100644 --- a/pennylane_lightning/core/src/utils/SharedLibLoader.hpp +++ b/pennylane_lightning/core/src/utils/SharedLibLoader.hpp @@ -84,7 +84,7 @@ class SharedLibLoader final { // TODO add windows support #elif defined(_MSC_VER) void *sym = reinterpret_cast(GetProcAddress(handle_, symbol.c_str())); - PL_ABORT_IF(!handle_, std::to_string(GetLastError())); + //PL_ABORT_IF(!sym, std::to_string(GetLastError())); #endif return sym; } From ff1ae2c5ba5b2fd2dba669e8b2ad89fda7dd9b1d Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Sun, 31 Mar 2024 13:56:57 -0400 Subject: [PATCH 427/428] test --- .../simulators/lightning_qubit/utils/IntegerInterval.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pennylane_lightning/core/src/simulators/lightning_qubit/utils/IntegerInterval.hpp b/pennylane_lightning/core/src/simulators/lightning_qubit/utils/IntegerInterval.hpp index 0fdbef6fd7..cc5401d614 100644 --- a/pennylane_lightning/core/src/simulators/lightning_qubit/utils/IntegerInterval.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_qubit/utils/IntegerInterval.hpp @@ -43,9 +43,9 @@ template class IntegerInterval { return (min_ <= test_val) && (test_val < max_); } - [[nodiscard]] IntegerType min() const { return min_; } + [[nodiscard]] IntegerType MIN() const { return min_; } - [[nodiscard]] IntegerType max() const { return max_; } + [[nodiscard]] IntegerType MAX() const { return max_; } }; /** @@ -109,7 +109,7 @@ constexpr auto full_domain() -> IntegerInterval { template bool is_disjoint(const IntegerInterval &interval1, const IntegerInterval &interval2) { - return (interval1.max() <= interval2.min()) || - (interval2.max() <= interval1.min()); + return (interval1.MAX() <= interval2.MIN()) || + (interval2.MAX() <= interval1.MIN()); } } // namespace Pennylane::LightningQubit::Util From d60d04dba8c1d11f4c552ea7081b3baa1abc75b0 Mon Sep 17 00:00:00 2001 From: Shuli Shu <08cnbj@gmail.com> Date: Sun, 31 Mar 2024 14:11:22 -0400 Subject: [PATCH 428/428] quick test --- pennylane_lightning/core/src/utils/Memory.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pennylane_lightning/core/src/utils/Memory.hpp b/pennylane_lightning/core/src/utils/Memory.hpp index b9886d5a3d..60e6c58656 100644 --- a/pennylane_lightning/core/src/utils/Memory.hpp +++ b/pennylane_lightning/core/src/utils/Memory.hpp @@ -209,6 +209,9 @@ struct Undefined {}; ///@cond DEV template struct commonAlignmentHelper { + constexpr static size_t required_alignment = + std::alignment_of_v; + constexpr static size_t value = std::max( TypeList::Type::template required_alignment, commonAlignmentHelper::value);