Skip to content

Commit

Permalink
Update 0.25.1 (#2973)
Browse files Browse the repository at this point in the history
* changes

* Copy gpu torch fix

* Spacing

* Trigger CI

* Trigger GPU

* Add gpu tests

* Update gpu action
  • Loading branch information
rmoyard authored Aug 18, 2022
1 parent 0e03b81 commit 49cb810
Show file tree
Hide file tree
Showing 7 changed files with 232 additions and 14 deletions.
122 changes: 122 additions & 0 deletions .github/workflows/tests-gpu.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
name: GPU Tests
on:
push:
branches:
- master
pull_request:
types:
- opened
- reopened
- labeled
- synchronize

concurrency:
group: gpu-test-${{ github.ref }}
cancel-in-progress: true

env:
TORCH_VERSION: 1.11.0+cu113

jobs:
gpu-tests:
runs-on:
- self-hosted
- ubuntu-22.04
- gpu

if: >-
${{
github.event_name == 'push' ||
(
github.event_name == 'pull_request' &&
contains(github.event.pull_request.labels.*.name, 'gpu') &&
github.event.pull_request.state == 'open'
)
}}
strategy:
max-parallel: 2
matrix:
python-version:
- 3.7
- 3.8
- 3.9
- '3.10'

env:
SUITE: gpu

steps:
- name: Update PATH and LD_LIBRARY_PATH
run: |
echo "$HOME/.local/bin" >> $GITHUB_PATH
echo "/usr/local/cuda/bin" >> $GITHUB_PATH
echo "LD_LIBRARY_PATH=/usr/local/cuda/lib64${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}" >> $GITHUB_ENV
- name: Checkout Repo
uses: actions/checkout@v3
with:
fetch-depth: 1

- name: Setup Python
id: setup_python
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}

# 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
id: setup_venv
env:
VENV_NAME: venv_${{ steps.setup_python.outputs.python-version }}_${{ github.sha }}
run: |
# Clear any pre-existing venvs
rm -rf venv_*
# Create new venv for this workflow_run
python --version
python -m venv ${{ env.VENV_NAME }}
# Add the venv to PATH for subsequent steps
echo ${{ env.VENV_NAME }}/bin >> $GITHUB_PATH
# Adding venv name as an output for subsequent steps to reference if needed
echo "::set-output name=venv_name::${{ env.VENV_NAME }}"
- name: Display Python-Path
run: |
which python
which pip
- name: Install dependencies
run: |
python -m pip install --upgrade pip && pip install wheel --upgrade
pip install -r requirements-ci.txt --upgrade
pip install -r requirements-dev.txt --upgrade
- name: Install Torch
run: |
python -m pip install torch==${{ env.TORCH_VERSION }} -f https://download.pytorch.org/whl/cu113/torch_stable.html
- name: Install PennyLane
run: |
python setup.py bdist_wheel
pip install dist/PennyLane*.whl
- name: Install Lightning-master
run: python -m pip install -i https://test.pypi.org/simple/ PennyLane-Lightning --pre --upgrade

- name: Run tests
run: python -m pytest -m "${{ env.SUITE }}" tests

# Cleans up the working directory so subsequent jobs on same runner get a clean working directory
# This step runs even if any of the previous steps fail
- name: Cleanup
if: always()
run: |
rm -rf ${{ steps.setup_venv.outputs.venv_name }}
rm -rf *
rm -rf .git
rm -rf .gitignore
rm -rf .github
2 changes: 1 addition & 1 deletion doc/releases/changelog-0.25.0.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
:orphan:

# Release 0.25.0 (current release)
# Release 0.25.0

<h3>New features since last release</h3>

Expand Down
4 changes: 4 additions & 0 deletions pennylane/math/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,13 @@
from .multi_dispatch import (
_multi_dispatch,
multi_dispatch,
array,
block_diag,
concatenate,
diag,
dot,
einsum,
eye,
frobenius_inner_product,
get_trainable_indices,
ones_like,
Expand Down Expand Up @@ -89,6 +91,7 @@ def __getattr__(name):
"multi_dispatch",
"allclose",
"allequal",
"array",
"block_diag",
"cast",
"cast_like",
Expand All @@ -98,6 +101,7 @@ def __getattr__(name):
"diag",
"dot",
"einsum",
"eye",
"fidelity",
"frobenius_inner_product",
"get_interface",
Expand Down
35 changes: 34 additions & 1 deletion pennylane/math/multi_dispatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,35 @@
from .utils import cast, get_interface, requires_grad


# pylint:disable=redefined-outer-name
def array(*args, like=None, **kwargs):
"""Creates an array or tensor object of the target framework.
This method preserves the Torch device used.
Returns:
tensor_like: the tensor_like object of the framework
"""
res = np.array(*args, like=like, **kwargs)
if like is not None and get_interface(like) == "torch":
res = res.to(device=like.device)
return res


def eye(*args, like=None, **kwargs):
"""Creates an identity array or tensor object of the target framework.
This method preserves the Torch device used.
Returns:
tensor_like: the tensor_like object of the framework
"""
res = np.eye(*args, like=like, **kwargs)
if like is not None and get_interface(like) == "torch":
res = res.to(device=like.device)
return res


def _multi_dispatch(values):
"""Determines the correct framework to dispatch to given a
sequence of tensor-like objects.
Expand Down Expand Up @@ -425,7 +454,11 @@ def get_trainable_indices(values, like=None):

if not any(isinstance(v, jax.core.Tracer) for v in values):
# No JAX tracing is occuring; treat all `DeviceArray` objects as trainable.
trainable = lambda p, **kwargs: isinstance(p, jax.numpy.DeviceArray)

# pylint: disable=function-redefined,unused-argument
def trainable(p, **kwargs):
return isinstance(p, jax.numpy.DeviceArray)

else:
# JAX tracing is occuring; use the default behaviour (only traced arrays
# are treated as trainable). This is required to ensure that `jax.grad(func, argnums=...)
Expand Down
19 changes: 15 additions & 4 deletions pennylane/ops/qubit/parametric_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -2672,12 +2672,23 @@ def compute_matrix(phi): # pylint: disable=arguments-differ
s = qml.math.cast_like(s, 1j)

js = 1j * s
r_term = qml.math.cast_like(
qml.math.array(
[
[0.0, 0.0, 0.0, 1.0],
[0.0, 0.0, -1.0, 0.0],
[0.0, -1.0, 0.0, 0.0],
[1.0, 0.0, 0.0, 0.0],
],
like=js,
),
1j,
)

if qml.math.ndim(phi) == 0:
return c * np.eye(4) + js * np.diag([1, -1, -1, 1])[::-1]
return c * qml.math.cast_like(qml.math.eye(4, like=c), c) + js * r_term

return qml.math.tensordot(c, np.eye(4), axes=0) + qml.math.tensordot(
js, np.diag([1, -1, -1, 1])[::-1], axes=0
)
return qml.math.tensordot(c, np.eye(4), axes=0) + qml.math.tensordot(js, r_term, axes=0)

def adjoint(self):
(phi,) = self.parameters
Expand Down
49 changes: 41 additions & 8 deletions tests/devices/test_default_qubit_torch.py
Original file line number Diff line number Diff line change
Expand Up @@ -735,7 +735,9 @@ def test_single_qubit_parameters_broadcasted_par(
dev.apply(queue)

res = dev.state
op_mat = torch.tensor([func(t) for t in theta], dtype=torch.complex128, device=torch_device)
op_mat = torch.tensor(
np.array([func(t) for t in theta]), dtype=torch.complex128, device=torch_device
)
expected = qml.math.einsum("lij,j->li", op_mat, state)
assert torch.allclose(res, expected, atol=tol, rtol=0)

Expand All @@ -754,7 +756,9 @@ def test_single_qubit_parameters_broadcasted_both(
dev.apply(queue)

res = dev.state
op_mat = torch.tensor([func(t) for t in theta], dtype=torch.complex128, device=torch_device)
op_mat = torch.tensor(
np.array([func(t) for t in theta]), dtype=torch.complex128, device=torch_device
)
expected = qml.math.einsum("lij,lj->li", op_mat, state)
assert torch.allclose(res, expected, atol=tol, rtol=0)

Expand Down Expand Up @@ -1136,10 +1140,10 @@ def test_multi_mode_hermitian_expectation(self, device, torch_device, theta, phi

dev = device(wires=2, torch_device=torch_device)

par1 = theta.to(device=torch_device)
par2 = phi.to(device=torch_device)
theta = theta.to(device=torch_device)
phi = phi.to(device=torch_device)
with qml.tape.QuantumTape() as tape:
queue = [qml.RY(par1, wires=0), qml.RY(par2, wires=1), qml.CNOT(wires=[0, 1])]
queue = [qml.RY(theta, wires=0), qml.RY(phi, wires=1), qml.CNOT(wires=[0, 1])]
observables = [qml.expval(qml.Hermitian(Hermit_mat2, wires=[0, 1]))]

res = dev.execute(tape)
Expand All @@ -1160,6 +1164,9 @@ def test_paulix_pauliy(self, device, torch_device, theta, phi, varphi, tol):
"""Test that a tensor product involving PauliX and PauliY works correctly"""
dev = device(wires=3, torch_device=torch_device)
dev.reset()
theta = theta.to(device=torch_device)
phi = phi.to(device=torch_device)
varphi = varphi.to(device=torch_device)

obs = qml.PauliX(0) @ qml.PauliY(2)

Expand All @@ -1184,6 +1191,8 @@ def test_pauliz_identity(self, device, torch_device, theta, phi, varphi, tol):
"""Test that a tensor product involving PauliZ and Identity works correctly"""
dev = device(wires=3, torch_device=torch_device)
dev.reset()
phi = phi.to(device=torch_device)
varphi = varphi.to(device=torch_device)

obs = qml.PauliZ(0) @ qml.Identity(1) @ qml.PauliZ(2)

Expand All @@ -1207,6 +1216,10 @@ def test_pauliz_identity(self, device, torch_device, theta, phi, varphi, tol):
def test_pauliz_hadamard(self, device, torch_device, theta, phi, varphi, tol):
"""Test that a tensor product involving PauliZ and PauliY and hadamard works correctly"""
dev = device(wires=3, torch_device=torch_device)
theta = theta.to(device=torch_device)
phi = phi.to(device=torch_device)
varphi = varphi.to(device=torch_device)

obs = qml.PauliZ(0) @ qml.Hadamard(1) @ qml.PauliY(2)

dev.reset()
Expand All @@ -1233,6 +1246,9 @@ def test_hermitian(self, device, torch_device, theta, phi, varphi, tol):
"""Test that a tensor product involving qml.Hermitian works correctly"""
dev = device(wires=3, torch_device=torch_device)
dev.reset()
theta = theta.to(device=torch_device)
phi = phi.to(device=torch_device)
varphi = varphi.to(device=torch_device)

Hermit_mat3 = torch.tensor(
[
Expand Down Expand Up @@ -1272,6 +1288,10 @@ def test_hermitian_hermitian(self, device, torch_device, theta, phi, varphi, tol
"""Test that a tensor product involving two Hermitian matrices works correctly"""
dev = device(wires=3, torch_device=torch_device)

theta = theta.to(device=torch_device)
phi = phi.to(device=torch_device)
varphi = varphi.to(device=torch_device)

A1 = torch.tensor([[1, 2], [2, 4]], dtype=torch.complex128)

A2 = torch.tensor(
Expand All @@ -1283,6 +1303,8 @@ def test_hermitian_hermitian(self, device, torch_device, theta, phi, varphi, tol
],
dtype=torch.complex128,
)
A1 = A1.to(device=torch_device)
A2 = A2.to(device=torch_device)

obs = qml.Hermitian(A1, wires=[0]) @ qml.Hermitian(A2, wires=[1, 2])

Expand Down Expand Up @@ -1328,10 +1350,14 @@ def test_hermitian_identity_expectation(self, device, torch_device, theta, phi,
"""Test that a tensor product involving an Hermitian matrix and the identity works correctly"""
dev = device(wires=2, torch_device=torch_device)

theta = theta.to(device=torch_device)
phi = phi.to(device=torch_device)

A = torch.tensor(
[[1.02789352, 1.61296440 - 0.3498192j], [1.61296440 + 0.3498192j, 1.23920938 + 0j]],
dtype=torch.complex128,
)
A = A.to(device=torch_device)

obs = qml.Hermitian(A, wires=[0]) @ qml.Identity(wires=[1])

Expand All @@ -1356,12 +1382,18 @@ def test_hermitian_two_wires_identity_expectation(
):
"""Test that a tensor product involving an Hermitian matrix for two wires and the identity works correctly"""
dev = device(wires=3, torch_device=torch_device)
theta = theta.to(device=torch_device)
phi = phi.to(device=torch_device)

A = torch.tensor(
[[1.02789352, 1.61296440 - 0.3498192j], [1.61296440 + 0.3498192j, 1.23920938 + 0j]],
dtype=torch.complex128,
)
A = A.to(device=torch_device)

Identity = torch.tensor([[1, 0], [0, 1]])
Identity = Identity.to(device=torch_device)

H = torch.kron(torch.kron(Identity, Identity), A)
obs = qml.Hermitian(H, wires=[2, 1, 0])

Expand Down Expand Up @@ -1397,12 +1429,12 @@ def test_var(self, device, torch_device, theta, phi, varphi, tol):
"""Tests for variance calculation"""
dev = device(wires=1, torch_device=torch_device)

par1 = theta.to(device=torch_device)
par2 = phi.to(device=torch_device)
theta = theta.to(device=torch_device)
phi = phi.to(device=torch_device)

# test correct variance for <Z> of a rotated state
with qml.tape.QuantumTape() as tape:
queue = [qml.RX(par1, wires=0), qml.RY(par2, wires=0)]
queue = [qml.RX(theta, wires=0), qml.RY(phi, wires=0)]
observables = [qml.var(qml.PauliZ(wires=[0]))]

res = dev.execute(tape)
Expand Down Expand Up @@ -1747,6 +1779,7 @@ def circuit(params):

# Pass a Torch Variable to the qfunc
params = torch.tensor([theta], device=torch_device)
params = params.to(device=torch_device)
circuit(params)
res = dev.state
expected = torch.tensor(func(theta), dtype=torch.complex128, device=torch_device) @ state
Expand Down
Loading

0 comments on commit 49cb810

Please sign in to comment.