Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The adjoint method in lightning supports qubit unitaries #540

Merged
merged 52 commits into from
Nov 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
f5c933d
Initial commit to add adjoint diff support for circuits with qubit-un…
vincentmr Oct 31, 2023
fd25cb0
Merge branch 'master' into feature/adjoint_diff_unitary
vincentmr Oct 31, 2023
5f1d9ed
Auto update version
github-actions[bot] Oct 31, 2023
b0b4972
Try nick-fields/retry for clang-tidy check.
vincentmr Oct 31, 2023
2a768d8
Fix LQubit c++ tests.
vincentmr Oct 31, 2023
5dbed67
Generalize unitary addiff tests to L-Kokkos.
vincentmr Oct 31, 2023
2b51bb1
Revert condition for branching in applyOperation.
vincentmr Oct 31, 2023
00d1ad8
Fix pip install and format
vincentmr Oct 31, 2023
a821719
remove prep argument
albi3ro Oct 31, 2023
6b56438
Auto update version
github-actions[bot] Oct 31, 2023
3e11ef5
Trigger CI
AmintorDusko Oct 31, 2023
ba8c405
Merge remote-tracking branch 'origin/remove-prep' into feature/adjoin…
vincentmr Oct 31, 2023
c1d16b1
Generalize fix and tests to L-GPU.
vincentmr Oct 31, 2023
33712b6
Set timeout_minutes for retry.
vincentmr Oct 31, 2023
b434ae6
Add L-GPU-MPI [skip ci].
vincentmr Oct 31, 2023
5a9f3b7
trigger CI
vincentmr Oct 31, 2023
d0ffa8b
Auto update version
github-actions[bot] Oct 31, 2023
109d29c
Merge branch 'master' into feature/adjoint_diff_unitary
vincentmr Oct 31, 2023
a15acab
Apply prep fix to mpitests.
vincentmr Oct 31, 2023
fbfd813
Add C++ coverage for QubitUnitary in addiff.
vincentmr Nov 1, 2023
c7bb460
Add test_qubit_unitary docstring [skip ci].
vincentmr Nov 1, 2023
48eccf0
Update changelog [skip ci]
vincentmr Nov 1, 2023
2651ffb
Add diff of unitary tests.
vincentmr Nov 1, 2023
45a6554
Try removing gcc-13.
vincentmr Nov 1, 2023
792998f
Remove annoying headers.
vincentmr Nov 1, 2023
b905c67
Revert changes to format.yml.
vincentmr Nov 1, 2023
0933b22
Update pennylane_lightning/core/src/simulators/lightning_gpu/StateVec…
vincentmr Nov 2, 2023
09ebb81
Update changelog [skip ci]
vincentmr Nov 2, 2023
a8f99e8
Remove unused tol variable [skip ci]
vincentmr Nov 2, 2023
7b6ae24
Revert changes to the opsdata init in adjoint tests.
vincentmr Nov 2, 2023
23161f9
Auto update version
github-actions[bot] Nov 2, 2023
6685361
nuni => n_targets param is less confusing.
vincentmr Nov 2, 2023
f326e79
Merge branch 'master' into feature/adjoint_diff_unitary
vincentmr Nov 2, 2023
cfe228d
Revert changes to std::complex
vincentmr Nov 2, 2023
aa49202
Increase tests_windows timeouts.
vincentmr Nov 2, 2023
c82ec95
Revert to 30 min.
vincentmr Nov 2, 2023
d2d7db2
Try removing {} init.
vincentmr Nov 2, 2023
c38735d
if-no-files-found: error in uploads.
vincentmr Nov 7, 2023
165d44a
Fix coverage.xml name
vincentmr Nov 7, 2023
4cfec34
Merge branch 'master' into feature/adjoint_diff_unitary
vincentmr Nov 10, 2023
dd7be3c
Auto update version
github-actions[bot] Nov 10, 2023
e638d0e
Fix parameter counting for circuits with QubitUnitaries.
vincentmr Nov 10, 2023
77043df
Merge branch 'master' into feature/adjoint_diff_unitary
vincentmr Nov 10, 2023
ac212fd
Auto update version
github-actions[bot] Nov 10, 2023
54e6ccf
trigger ci
vincentmr Nov 10, 2023
3093dd9
Update .github/CHANGELOG.md [skip ci]
vincentmr Nov 13, 2023
5b4280e
Use name instead of class to serialize QubitUnitary.
vincentmr Nov 13, 2023
5dd3b99
Update CHANGELOG.md [skip ci]
vincentmr Nov 14, 2023
f1f4aa1
Update CHANGELOG.md [skip ci]
vincentmr Nov 14, 2023
f657026
Add float32 mpitests
vincentmr Nov 14, 2023
ec7038e
Rename fixture function.
vincentmr Nov 14, 2023
b9a205a
trigger ci
vincentmr Nov 15, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .github/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,19 @@

### New features since last release

* `qml.QubitUnitary` operators can be included in a circuit differentiated with the adjoint method. Lightning handles circuits with arbitrary non-differentiable `qml.QubitUnitary` operators. 1,2-qubit `qml.QubitUnitary` operators with differentiable parameters can be differentiated using decomposition.
[(#540)] (https://github.com/PennyLaneAI/pennylane-lightning/pull/540)

### Breaking changes

* Overload `applyOperation` with a fifth `matrix` argument to all state vector classes to support arbitrary operations in `AdjointJacobianBase`.
[(#540)] (https://github.com/PennyLaneAI/pennylane-lightning/pull/540)

### Improvements

* Modify `setup.py` to use backend-specific build directory (`f"build_{backend}"`) to accelerate rebuilding backends in alternance.
[(#540)] (https://github.com/PennyLaneAI/pennylane-lightning/pull/540)

* Update Dockerfile and rewrite the `build-wheel-lightning-gpu` stage to build Lightning-GPU from the `pennylane-lightning` monorepo.
[(#539)] (https://github.com/PennyLaneAI/pennylane-lightning/pull/539)

Expand All @@ -25,6 +34,9 @@

### Bug fixes

* Move deprecated `stateprep` `QuantumScript` argument into the operation list in `mpitests/test_adjoint_jacobian.py`.
[(#540)] (https://github.com/PennyLaneAI/pennylane-lightning/pull/540)

* Fix MPI Python unit tests for the adjoint method.
[(#538)](https://github.com/PennyLaneAI/pennylane-lightning/pull/538)

Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/tests_gpu_cu11.yml
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,8 @@ jobs:
with:
name: ubuntu-codecov-results-python
path: ./main/coverage-${{ github.job }}-${{ matrix.pl_backend }}.xml

if-no-files-found: error

upload-to-codecov-linux-python:
needs: [pythontestswithLGPU]
name: Upload coverage data to codecov
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/tests_gpu_kokkos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ jobs:
pl-device-test --device ${DEVICENAME} --skip-ops --shots=20000 $COVERAGE_FLAGS --cov-append 2> /dev/null || echo Something went wrong with pl-device-test shot=20000
pl-device-test --device ${DEVICENAME} --shots=None --skip-ops $COVERAGE_FLAGS --cov-append 2> /dev/null || echo Something went wrong with pl-device-test shot=None
PL_DEVICE=${DEVICENAME} python -m pytest tests/ $COVERAGE_FLAGS 2> /dev/null || echo Something went wrong with Pytest
mv coverage.xml coverage-${{ github.job }}.xml
mv coverage.xml coverage-${{ github.job }}-${{ matrix.pl_backend }}.xml

- name: Install all backend devices
if: ${{ matrix.pl_backend == 'all' }}
Expand Down Expand Up @@ -345,3 +345,4 @@ jobs:
with:
name: ubuntu-codecov-results-python
path: ./main/coverage-${{ github.job }}-${{ matrix.pl_backend }}.xml
if-no-files-found: error
5 changes: 4 additions & 1 deletion .github/workflows/tests_linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ jobs:
with:
name: ubuntu-codecov-results-python
path: ./main/coverage-${{ github.job }}-${{ matrix.pl_backend }}.xml
if-no-files-found: error

cpptestswithOpenBLAS:
if: ${{ !contains(fromJSON('["workflow_call"]'), github.event_name) }}
Expand Down Expand Up @@ -323,6 +324,7 @@ jobs:
with:
name: ubuntu-codecov-results-python
path: ./main/coverage-${{ github.job }}-${{ matrix.pl_backend }}.xml
if-no-files-found: error

build_and_cache_Kokkos:
name: "Build and cache Kokkos"
Expand Down Expand Up @@ -512,7 +514,7 @@ jobs:
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.xml coverage-${{ github.job }}.xml
mv coverage.xml coverage-${{ github.job }}-${{ matrix.pl_backend }}.xml

- name: Install all backend devices
if: ${{ matrix.pl_backend == 'all' }}
Expand Down Expand Up @@ -541,6 +543,7 @@ jobs:
with:
name: ubuntu-codecov-results-python
path: ./main/coverage-${{ github.job }}-${{ matrix.pl_backend }}.xml
if-no-files-found: error

upload-to-codecov-linux-python:
needs: [pythontests, pythontestswithOpenBLAS, pythontestswithKokkos]
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/tests_linux_x86_mpi_gpu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -138,16 +138,16 @@ jobs:
uses: actions/upload-artifact@v3
if: always()
with:
if-no-files-found: error
name: ubuntu-tests-reports
path: ./Build/tests/results/
if-no-files-found: error

- name: Upload code coverage results
uses: actions/upload-artifact@v3
with:
if-no-files-found: error
name: ubuntu-codecov-results-cpp
path: ./Build/coverage-${{ github.job }}-lightning_gpu_${{ matrix.mpilib }}.info
if-no-files-found: error

- name: Cleanup
if: always()
Expand Down
7 changes: 5 additions & 2 deletions .github/workflows/tests_windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,14 @@ jobs:
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
Expand Down Expand Up @@ -218,12 +219,14 @@ jobs:
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

upload-to-codecov-windows:
needs: [cpptests, cpptestswithkokkos]
Expand Down
128 changes: 116 additions & 12 deletions mpitests/test_adjoint_jacobian.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,18 @@
)


@pytest.fixture(name="dev", params=fixture_params)
def fixture_dev(request):
"""Returns a PennyLane device."""
return qml.device(
device_name,
wires=8,
mpi=True,
c_dtype=request.param[0],
batch_obs=request.param[1],
)


def Rx(theta):
r"""One-qubit rotation about the x axis.

Expand Down Expand Up @@ -78,17 +90,6 @@ def Rz(theta):
class TestAdjointJacobian: # pylint: disable=too-many-public-methods
"""Tests for the adjoint_jacobian method"""

@pytest.fixture(params=fixture_params)
def dev(self, request):
"""Returns a PennyLane device."""
return qml.device(
device_name,
wires=8,
mpi=True,
c_dtype=request.param[0],
batch_obs=request.param[1],
)

def test_not_expval(self, dev):
"""Test if a QuantumFunctionError is raised for a tape with measurements that are not
expectation values"""
Expand Down Expand Up @@ -189,7 +190,7 @@ def test_pauli_rotation_gradient(self, stateprep, G, theta, dev):
)

tape = qml.tape.QuantumScript(
[G(theta, 0)], [qml.expval(qml.PauliZ(0))], [stateprep(random_state, 0)]
[stateprep(random_state, 0), G(theta, 0)], [qml.expval(qml.PauliZ(0))]
)

tape.trainable_params = {1}
Expand Down Expand Up @@ -1381,3 +1382,106 @@ def circuit(params):
comm.Barrier()

assert np.allclose(j_cpu, j_gpu)


@pytest.mark.parametrize("n_targets", range(1, 5))
def test_qubit_unitary(dev, n_targets):
"""Tests that ``qml.QubitUnitary`` can be included in circuits differentiated with the adjoint method."""
n_wires = len(dev.wires)
dev_def = qml.device("default.qubit.legacy", wires=n_wires)
h = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7
c_dtype = np.complex64 if dev.R_DTYPE == np.float32 else np.complex128

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.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))

comm = MPI.COMM_WORLD
par = comm.bcast(par, root=0)
U = comm.bcast(U, root=0)
init_state = comm.bcast(init_state, root=0)

init_state = np.array(init_state, requires_grad=False, dtype=c_dtype)
U = np.array(U, requires_grad=False, dtype=c_dtype)
obs = qml.operation.Tensor(*(qml.PauliZ(i) for i in range(n_wires)))

def circuit(x):
qml.StatePrep(init_state, wires=range(n_wires))
for i in range(n_wires // 2):
qml.RY(x[i], wires=i)
qml.QubitUnitary(U, wires=range(n_targets))
for i in range(n_wires // 2, n_wires):
qml.RY(x[i], wires=i)
return qml.expval(obs)

circ = qml.QNode(circuit, dev, diff_method="adjoint")
circ_ps = qml.QNode(circuit, dev, diff_method="parameter-shift")
circ_def = qml.QNode(circuit, dev_def, diff_method="adjoint")
jac = qml.jacobian(circ)(par)
jac_ps = qml.jacobian(circ_ps)(par)
jac_def = qml.jacobian(circ_def)(par)

comm.Barrier()

assert len(jac) == n_wires
assert not np.allclose(jac, 0.0)
assert np.allclose(jac, jac_ps, atol=h, rtol=0)
assert np.allclose(jac, jac_def, atol=h, rtol=0)


@pytest.mark.parametrize("n_targets", [1, 2])
def test_diff_qubit_unitary(dev, n_targets):
"""Tests that ``qml.QubitUnitary`` can be differentiated with the adjoint method."""
n_wires = len(dev.wires)
dev_def = qml.device("default.qubit", wires=n_wires)
h = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7
c_dtype = np.complex64 if dev.R_DTYPE == np.float32 else np.complex128

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.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))

comm = MPI.COMM_WORLD
par = comm.bcast(par, root=0)
U = comm.bcast(U, root=0)
init_state = comm.bcast(init_state, root=0)

init_state = np.array(init_state, requires_grad=False, dtype=c_dtype)
U = np.array(U, requires_grad=False, dtype=c_dtype)
obs = qml.operation.Tensor(*(qml.PauliZ(i) for i in range(n_wires)))

def circuit(x, u_mat):
qml.StatePrep(init_state, wires=range(n_wires))
for i in range(n_wires // 2):
qml.RY(x[i], wires=i)
qml.QubitUnitary(u_mat, wires=range(n_targets))
for i in range(n_wires // 2, n_wires):
qml.RY(x[i], wires=i)
return qml.expval(obs)

circ = qml.QNode(circuit, dev, diff_method="adjoint")
circ_def = qml.QNode(circuit, dev_def, diff_method="adjoint")
circ_fd = qml.QNode(circuit, dev, diff_method="finite-diff", h=h)
circ_ps = qml.QNode(circuit, dev, diff_method="parameter-shift")
jacs = qml.jacobian(circ)(par, U)
jacs_def = qml.jacobian(circ_def)(par, U)
jacs_fd = qml.jacobian(circ_fd)(par, U)
jacs_ps = qml.jacobian(circ_ps)(par, U)

comm.Barrier()

for jac, jac_def, jac_fd, jac_ps in zip(jacs, jacs_def, jacs_fd, jacs_ps):
assert not np.allclose(jac, 0.0)
assert np.allclose(jac, jac_fd, atol=h, rtol=0)
assert np.allclose(jac, jac_ps, atol=h, rtol=0)
assert np.allclose(jac, jac_def, atol=h, rtol=0)
9 changes: 6 additions & 3 deletions pennylane_lightning/core/_serialize.py
Original file line number Diff line number Diff line change
Expand Up @@ -307,11 +307,14 @@
for single_op in op_list:
name = single_op.name
names.append(name)

if not hasattr(self.sv_type, 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))
vincentmr marked this conversation as resolved.
Show resolved Hide resolved
elif not hasattr(self.sv_type, name):

Check warning on line 315 in pennylane_lightning/core/_serialize.py

View check run for this annotation

Codecov / codecov/patch

pennylane_lightning/core/_serialize.py#L312-L315

Added lines #L312 - L315 were not covered by tests
params.append([])
mats.append(matrix(single_op))

else:
params.append(single_op.parameters)
mats.append([])
Expand Down
2 changes: 1 addition & 1 deletion pennylane_lightning/core/_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@
Version number (major.minor.patch[-label])
"""

__version__ = "0.34.0-dev6"
__version__ = "0.34.0-dev7"
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ template <class StateVectorT, class Derived> class AdjointJacobianBase {
state.applyOperation(operations.getOpsName()[op_idx],
operations.getOpsWires()[op_idx],
operations.getOpsInverses()[op_idx] ^ adj,
operations.getOpsParams()[op_idx]);
operations.getOpsParams()[op_idx],
operations.getOpsMatrices()[op_idx]);
vincentmr marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand All @@ -79,7 +80,8 @@ template <class StateVectorT, class Derived> class AdjointJacobianBase {
state.applyOperation(operations.getOpsName()[op_idx],
operations.getOpsWires()[op_idx],
!operations.getOpsInverses()[op_idx],
operations.getOpsParams()[op_idx]);
operations.getOpsParams()[op_idx],
operations.getOpsMatrices()[op_idx]);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -351,8 +351,10 @@ template <typename TypeList> void testAdjointJacobian() {
"PauliX", std::vector<size_t>{1}),
std::make_shared<NamedObs<StateVectorT>>(
"PauliX", std::vector<size_t>{2}));
std::vector<ComplexT> cnot{1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0};
auto ops = OpsData<StateVectorT>(
{"RZ", "RY", "RZ", "CNOT", "CNOT", "RZ", "RY", "RZ"},
{"RZ", "RY", "RZ", "QubitUnitary", "CNOT", "RZ", "RY", "RZ"},
{{param[0]},
{param[1]},
{param[2]},
Expand All @@ -362,7 +364,9 @@ template <typename TypeList> void testAdjointJacobian() {
{param[1]},
{param[2]}},
{{0}, {0}, {0}, {0, 1}, {1, 2}, {1}, {1}, {1}},
{false, false, false, false, false, false, false, false});
{false, false, false, false, false, false, false, false},
std::vector<std::vector<ComplexT>>{
{}, {}, {}, cnot, {}, {}, {}, {}});

JacobianData<StateVectorT> tape{
num_params, psi.getLength(), psi.getData(), {obs}, ops, tp};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,8 +249,10 @@ template <typename TypeList> void testAdjointJacobian() {
"PauliX", std::vector<size_t>{1}),
std::make_shared<NamedObsMPI<StateVectorT>>(
"PauliX", std::vector<size_t>{2}));
std::vector<ComplexT> cnot{1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0};
auto ops = OpsData<StateVectorT>(
{"RZ", "RY", "RZ", "CNOT", "CNOT", "RZ", "RY", "RZ"},
{"RZ", "RY", "RZ", "QubitUnitary", "CNOT", "RZ", "RY", "RZ"},
{{param[0]},
{param[1]},
{param[2]},
Expand All @@ -260,7 +262,9 @@ template <typename TypeList> void testAdjointJacobian() {
{param[1]},
{param[2]}},
{{0}, {0}, {0}, {0, 1}, {1, 2}, {1}, {1}, {1}},
{false, false, false, false, false, false, false, false});
{false, false, false, false, false, false, false, false},
std::vector<std::vector<ComplexT>>{
{}, {}, {}, cnot, {}, {}, {}, {}});

JacobianDataMPI<StateVectorT> tape{num_params, psi, {obs}, ops, tp};

Expand Down
Loading
Loading