|
42 | 42 | )
|
43 | 43 |
|
44 | 44 |
|
| 45 | +@pytest.fixture(name="dev", params=fixture_params) |
| 46 | +def fixture_dev(request): |
| 47 | + """Returns a PennyLane device.""" |
| 48 | + return qml.device( |
| 49 | + device_name, |
| 50 | + wires=8, |
| 51 | + mpi=True, |
| 52 | + c_dtype=request.param[0], |
| 53 | + batch_obs=request.param[1], |
| 54 | + ) |
| 55 | + |
| 56 | + |
45 | 57 | def Rx(theta):
|
46 | 58 | r"""One-qubit rotation about the x axis.
|
47 | 59 |
|
@@ -78,17 +90,6 @@ def Rz(theta):
|
78 | 90 | class TestAdjointJacobian: # pylint: disable=too-many-public-methods
|
79 | 91 | """Tests for the adjoint_jacobian method"""
|
80 | 92 |
|
81 |
| - @pytest.fixture(params=fixture_params) |
82 |
| - def dev(self, request): |
83 |
| - """Returns a PennyLane device.""" |
84 |
| - return qml.device( |
85 |
| - device_name, |
86 |
| - wires=8, |
87 |
| - mpi=True, |
88 |
| - c_dtype=request.param[0], |
89 |
| - batch_obs=request.param[1], |
90 |
| - ) |
91 |
| - |
92 | 93 | def test_not_expval(self, dev):
|
93 | 94 | """Test if a QuantumFunctionError is raised for a tape with measurements that are not
|
94 | 95 | expectation values"""
|
@@ -189,7 +190,7 @@ def test_pauli_rotation_gradient(self, stateprep, G, theta, dev):
|
189 | 190 | )
|
190 | 191 |
|
191 | 192 | tape = qml.tape.QuantumScript(
|
192 |
| - [G(theta, 0)], [qml.expval(qml.PauliZ(0))], [stateprep(random_state, 0)] |
| 193 | + [stateprep(random_state, 0), G(theta, 0)], [qml.expval(qml.PauliZ(0))] |
193 | 194 | )
|
194 | 195 |
|
195 | 196 | tape.trainable_params = {1}
|
@@ -1381,3 +1382,106 @@ def circuit(params):
|
1381 | 1382 | comm.Barrier()
|
1382 | 1383 |
|
1383 | 1384 | assert np.allclose(j_cpu, j_gpu)
|
| 1385 | + |
| 1386 | + |
| 1387 | +@pytest.mark.parametrize("n_targets", range(1, 5)) |
| 1388 | +def test_qubit_unitary(dev, n_targets): |
| 1389 | + """Tests that ``qml.QubitUnitary`` can be included in circuits differentiated with the adjoint method.""" |
| 1390 | + n_wires = len(dev.wires) |
| 1391 | + dev_def = qml.device("default.qubit.legacy", wires=n_wires) |
| 1392 | + h = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7 |
| 1393 | + c_dtype = np.complex64 if dev.R_DTYPE == np.float32 else np.complex128 |
| 1394 | + |
| 1395 | + np.random.seed(1337) |
| 1396 | + par = 2 * np.pi * np.random.rand(n_wires) |
| 1397 | + U = np.random.rand(2**n_targets, 2**n_targets) + 1j * np.random.rand( |
| 1398 | + 2**n_targets, 2**n_targets |
| 1399 | + ) |
| 1400 | + U, _ = np.linalg.qr(U) |
| 1401 | + init_state = np.random.rand(2**n_wires) + 1j * np.random.rand(2**n_wires) |
| 1402 | + init_state /= np.sqrt(np.dot(np.conj(init_state), init_state)) |
| 1403 | + |
| 1404 | + comm = MPI.COMM_WORLD |
| 1405 | + par = comm.bcast(par, root=0) |
| 1406 | + U = comm.bcast(U, root=0) |
| 1407 | + init_state = comm.bcast(init_state, root=0) |
| 1408 | + |
| 1409 | + init_state = np.array(init_state, requires_grad=False, dtype=c_dtype) |
| 1410 | + U = np.array(U, requires_grad=False, dtype=c_dtype) |
| 1411 | + obs = qml.operation.Tensor(*(qml.PauliZ(i) for i in range(n_wires))) |
| 1412 | + |
| 1413 | + def circuit(x): |
| 1414 | + qml.StatePrep(init_state, wires=range(n_wires)) |
| 1415 | + for i in range(n_wires // 2): |
| 1416 | + qml.RY(x[i], wires=i) |
| 1417 | + qml.QubitUnitary(U, wires=range(n_targets)) |
| 1418 | + for i in range(n_wires // 2, n_wires): |
| 1419 | + qml.RY(x[i], wires=i) |
| 1420 | + return qml.expval(obs) |
| 1421 | + |
| 1422 | + circ = qml.QNode(circuit, dev, diff_method="adjoint") |
| 1423 | + circ_ps = qml.QNode(circuit, dev, diff_method="parameter-shift") |
| 1424 | + circ_def = qml.QNode(circuit, dev_def, diff_method="adjoint") |
| 1425 | + jac = qml.jacobian(circ)(par) |
| 1426 | + jac_ps = qml.jacobian(circ_ps)(par) |
| 1427 | + jac_def = qml.jacobian(circ_def)(par) |
| 1428 | + |
| 1429 | + comm.Barrier() |
| 1430 | + |
| 1431 | + assert len(jac) == n_wires |
| 1432 | + assert not np.allclose(jac, 0.0) |
| 1433 | + assert np.allclose(jac, jac_ps, atol=h, rtol=0) |
| 1434 | + assert np.allclose(jac, jac_def, atol=h, rtol=0) |
| 1435 | + |
| 1436 | + |
| 1437 | +@pytest.mark.parametrize("n_targets", [1, 2]) |
| 1438 | +def test_diff_qubit_unitary(dev, n_targets): |
| 1439 | + """Tests that ``qml.QubitUnitary`` can be differentiated with the adjoint method.""" |
| 1440 | + n_wires = len(dev.wires) |
| 1441 | + dev_def = qml.device("default.qubit", wires=n_wires) |
| 1442 | + h = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7 |
| 1443 | + c_dtype = np.complex64 if dev.R_DTYPE == np.float32 else np.complex128 |
| 1444 | + |
| 1445 | + np.random.seed(1337) |
| 1446 | + par = 2 * np.pi * np.random.rand(n_wires) |
| 1447 | + U = np.random.rand(2**n_targets, 2**n_targets) + 1j * np.random.rand( |
| 1448 | + 2**n_targets, 2**n_targets |
| 1449 | + ) |
| 1450 | + U, _ = np.linalg.qr(U) |
| 1451 | + init_state = np.random.rand(2**n_wires) + 1j * np.random.rand(2**n_wires) |
| 1452 | + init_state /= np.sqrt(np.dot(np.conj(init_state), init_state)) |
| 1453 | + |
| 1454 | + comm = MPI.COMM_WORLD |
| 1455 | + par = comm.bcast(par, root=0) |
| 1456 | + U = comm.bcast(U, root=0) |
| 1457 | + init_state = comm.bcast(init_state, root=0) |
| 1458 | + |
| 1459 | + init_state = np.array(init_state, requires_grad=False, dtype=c_dtype) |
| 1460 | + U = np.array(U, requires_grad=False, dtype=c_dtype) |
| 1461 | + obs = qml.operation.Tensor(*(qml.PauliZ(i) for i in range(n_wires))) |
| 1462 | + |
| 1463 | + def circuit(x, u_mat): |
| 1464 | + qml.StatePrep(init_state, wires=range(n_wires)) |
| 1465 | + for i in range(n_wires // 2): |
| 1466 | + qml.RY(x[i], wires=i) |
| 1467 | + qml.QubitUnitary(u_mat, wires=range(n_targets)) |
| 1468 | + for i in range(n_wires // 2, n_wires): |
| 1469 | + qml.RY(x[i], wires=i) |
| 1470 | + return qml.expval(obs) |
| 1471 | + |
| 1472 | + circ = qml.QNode(circuit, dev, diff_method="adjoint") |
| 1473 | + circ_def = qml.QNode(circuit, dev_def, diff_method="adjoint") |
| 1474 | + circ_fd = qml.QNode(circuit, dev, diff_method="finite-diff", h=h) |
| 1475 | + circ_ps = qml.QNode(circuit, dev, diff_method="parameter-shift") |
| 1476 | + jacs = qml.jacobian(circ)(par, U) |
| 1477 | + jacs_def = qml.jacobian(circ_def)(par, U) |
| 1478 | + jacs_fd = qml.jacobian(circ_fd)(par, U) |
| 1479 | + jacs_ps = qml.jacobian(circ_ps)(par, U) |
| 1480 | + |
| 1481 | + comm.Barrier() |
| 1482 | + |
| 1483 | + for jac, jac_def, jac_fd, jac_ps in zip(jacs, jacs_def, jacs_fd, jacs_ps): |
| 1484 | + assert not np.allclose(jac, 0.0) |
| 1485 | + assert np.allclose(jac, jac_fd, atol=h, rtol=0) |
| 1486 | + assert np.allclose(jac, jac_ps, atol=h, rtol=0) |
| 1487 | + assert np.allclose(jac, jac_def, atol=h, rtol=0) |
0 commit comments