def test_push_x_gates_right(self): """Test that X-basis gates before controlled-X-type gates on targets get pushed ahead.""" def qfunc(): qml.PauliX(wires=2) qml.CNOT(wires=[0, 2]) qml.RX(0.2, wires=2) qml.Toffoli(wires=[0, 1, 2]) qml.SX(wires=1) qml.PauliX(wires=1) qml.CRX(0.1, wires=[0, 1]) transformed_qfunc = commute_controlled()(qfunc) ops = qml.transforms.make_tape(transformed_qfunc)().operations names_expected = [ "CNOT", "Toffoli", "PauliX", "RX", "CRX", "SX", "PauliX" ] wires_expected = [ Wires([0, 2]), Wires([0, 1, 2]), Wires(2), Wires(2), Wires([0, 1]), Wires(1), Wires(1), ] compare_operation_lists(ops, names_expected, wires_expected)
def test_push_z_gates_left(self): """Test that Z-basis after before controlled-Z-type gates on controls *and* targets get pushed behind.""" def qfunc(): qml.CZ(wires=[0, 2]) qml.PauliZ(wires=2) qml.S(wires=0) qml.CNOT(wires=[0, 1]) qml.CRZ(0.5, wires=[0, 1]) qml.RZ(0.2, wires=2) qml.T(wires=0) qml.PauliZ(wires=0) transformed_qfunc = commute_controlled(direction="left")(qfunc) ops = qml.transforms.make_tape(transformed_qfunc)().operations names_expected = [ "PauliZ", "S", "RZ", "T", "PauliZ", "CZ", "CNOT", "CRZ" ] wires_expected = [ Wires(2), Wires(0), Wires(2), Wires(0), Wires(0), Wires([0, 2]) ] + [Wires([0, 1])] * 2 compare_operation_lists(ops, names_expected, wires_expected)
def test_push_mixed_with_matrix(self, direction): """Test that arbitrary gates after controlled gates on controls *and* targets get properly pushed.""" def qfunc(): qml.PauliX(wires=1) qml.S(wires=0) qml.CZ(wires=[0, 1]) qml.CNOT(wires=[1, 0]) qml.PauliY(wires=1) qml.CRY(0.5, wires=[1, 0]) qml.PhaseShift(0.2, wires=0) qml.PauliY(wires=1) qml.T(wires=0) qml.CRZ(-0.3, wires=[0, 1]) qml.RZ(0.2, wires=0) qml.PauliZ(wires=0) qml.PauliX(wires=1) qml.CRY(0.2, wires=[1, 0]) transformed_qfunc = commute_controlled()(qfunc) original_ops = qml.transforms.make_tape(qfunc)().operations transformed_ops = qml.transforms.make_tape( transformed_qfunc)().operations assert len(original_ops) == len(transformed_ops) # Compare matrices matrix_expected = compute_matrix_from_ops_two_qubit(original_ops, wire_order=[0, 1]) matrix_obtained = compute_matrix_from_ops_two_qubit(transformed_ops, wire_order=[0, 1]) assert check_matrix_equivalence(matrix_expected, matrix_obtained)
def test_push_z_gates_right(self): """Test that Z-basis gates before controlled-Z-type gates on controls *and* targets get pushed ahead.""" def qfunc(): qml.PauliZ(wires=2) qml.S(wires=0) qml.CZ(wires=[0, 2]) qml.CNOT(wires=[0, 1]) qml.PhaseShift(0.2, wires=2) qml.T(wires=0) qml.PauliZ(wires=0) qml.CRZ(0.5, wires=[0, 1]) transformed_qfunc = commute_controlled()(qfunc) ops = qml.transforms.make_tape(transformed_qfunc)().operations names_expected = [ "CZ", "PauliZ", "CNOT", "PhaseShift", "CRZ", "S", "T", "PauliZ" ] wires_expected = ( [Wires([0, 2]), Wires(2), Wires([0, 1]), Wires(2)] + [Wires([0, 1])] + [Wires(0)] * 3) compare_operation_lists(ops, names_expected, wires_expected)
def test_compile_mixed_tape_qfunc_transform(self): """Test that we can interchange tape and qfunc transforms.""" wires = [0, 1, 2] qfunc = build_qfunc(wires) dev = qml.device("default.qubit", wires=wires) pipeline = [ commute_controlled(direction="right").tape_fn, cancel_inverses, merge_rotations().tape_fn, ] transformed_qfunc = compile(pipeline=pipeline)(qfunc) transformed_qnode = qml.QNode(transformed_qfunc, dev) transformed_result = transformed_qnode(0.3, 0.4, 0.5) names_expected = ["Hadamard", "CNOT", "RX", "CY", "PauliY"] wires_expected = [ Wires(wires[0]), Wires([wires[2], wires[1]]), Wires(wires[0]), Wires([wires[1], wires[2]]), Wires(wires[2]), ] compare_operation_lists(transformed_qnode.qtape.operations, names_expected, wires_expected)
def test_compile_multiple_passes(self, wires): """Test that running multiple passes produces the correct results.""" qfunc = build_qfunc(wires) dev = qml.device("default.qubit", wires=Wires(wires)) qnode = qml.QNode(qfunc, dev) # Rotation merging will not occur at all until commuting gates are # pushed through pipeline = [merge_rotations, commute_controlled(direction="left"), cancel_inverses] transformed_qfunc = compile(pipeline=pipeline, num_passes=2)(qfunc) transformed_qnode = qml.QNode(transformed_qfunc, dev) original_result = qnode(0.3, 0.4, 0.5) transformed_result = transformed_qnode(0.3, 0.4, 0.5) assert np.allclose(original_result, transformed_result) names_expected = ["Hadamard", "CNOT", "RX", "PauliY", "CY"] wires_expected = [ Wires(wires[0]), Wires([wires[2], wires[1]]), Wires(wires[0]), Wires(wires[2]), Wires([wires[1], wires[2]]), ] compare_operation_lists(transformed_qnode.qtape.operations, names_expected, wires_expected)
def test_compile_pipeline_with_non_default_arguments(self, wires): """Test that using non-default arguments returns the correct results.""" qfunc = build_qfunc(wires) dev = qml.device("default.qubit", wires=Wires(wires)) qnode = qml.QNode(qfunc, dev) pipeline = [ commute_controlled(direction="left"), cancel_inverses, merge_rotations(atol=1e-6), ] transformed_qfunc = compile(pipeline=pipeline)(qfunc) transformed_qnode = qml.QNode(transformed_qfunc, dev) original_result = qnode(0.3, 0.4, 0.5) transformed_result = transformed_qnode(0.3, 0.4, 0.5) assert np.allclose(original_result, transformed_result) names_expected = ["Hadamard", "CNOT", "RX", "PauliY", "CY"] wires_expected = [ Wires(wires[0]), Wires([wires[2], wires[1]]), Wires(wires[0]), Wires(wires[2]), Wires([wires[1], wires[2]]), ] compare_operation_lists(transformed_qnode.qtape.operations, names_expected, wires_expected)
def test_compile_decompose_into_basis_gates(self, wires): """Test that running multiple passes produces the correct results.""" qfunc = build_qfunc(wires) dev = qml.device("default.qubit", wires=Wires(wires)) qnode = qml.QNode(qfunc, dev) pipeline = [ commute_controlled(direction="left"), cancel_inverses, merge_rotations ] basis_set = ["CNOT", "RX", "RY", "RZ"] transformed_qfunc = compile(pipeline=pipeline, basis_set=basis_set)(qfunc) transformed_qnode = qml.QNode(transformed_qfunc, dev) original_result = qnode(0.3, 0.4, 0.5) transformed_result = transformed_qnode(0.3, 0.4, 0.5) assert np.allclose(original_result, transformed_result) names_expected = [ "RZ", "RX", "RZ", "RZ", "CNOT", "RX", "RZ", "RY", "RZ", "RY", "CNOT", "RY", "CNOT", ] wires_expected = [ Wires(wires[0]), Wires(wires[0]), Wires(wires[0]), Wires(wires[2]), Wires([wires[2], wires[1]]), Wires(wires[0]), Wires(wires[1]), Wires(wires[2]), Wires(wires[2]), Wires(wires[2]), Wires([wires[1], wires[2]]), Wires(wires[2]), Wires([wires[1], wires[2]]), ] compare_operation_lists(transformed_qnode.qtape.operations, names_expected, wires_expected)
def test_invalid_direction(self): """Test that any direction other than 'left' or 'right' raises an error.""" def qfunc(): qml.PauliX(wires=2) qml.CNOT(wires=[0, 2]) qml.RX(0.2, wires=2) transformed_qfunc = commute_controlled(direction="sideways")(qfunc) with pytest.raises(ValueError, match="must be 'left' or 'right'"): ops = qml.transforms.make_tape(transformed_qfunc)().operations
def test_gate_blocked_different_basis(self, direction): """Test that gates do not get pushed through controlled gates whose target bases don't match.""" def qfunc(): qml.PauliZ(wires="b") qml.CNOT(wires=[2, "b"]) qml.PauliY(wires="b") transformed_qfunc = commute_controlled(direction=direction)(qfunc) ops = qml.transforms.make_tape(transformed_qfunc)().operations names_expected = ["PauliZ", "CNOT", "PauliY"] wires_expected = [Wires("b"), Wires([2, "b"]), Wires("b")] compare_operation_lists(ops, names_expected, wires_expected)
def test_gate_with_no_basis(self, direction): """Test that gates with no basis specified are ignored.""" def qfunc(): qml.PauliX(wires=2) qml.ControlledQubitUnitary(np.array([[0, 1], [1, 0]]), control_wires=0, wires=2) qml.PauliX(wires=2) transformed_qfunc = commute_controlled(direction=direction)(qfunc) ops = qml.transforms.make_tape(transformed_qfunc)().operations names_expected = ["PauliX", "ControlledQubitUnitary", "PauliX"] wires_expected = [Wires(2), Wires([0, 2]), Wires(2)] compare_operation_lists(ops, names_expected, wires_expected)
def test_dont_push_x_gates(self, direction): """Test that X-basis gates before controlled-X-type gates on controls don't get pushed.""" def qfunc(): qml.PauliX(wires="a") qml.CNOT(wires=["a", "c"]) qml.RX(0.2, wires="a") qml.Toffoli(wires=["c", "a", "b"]) transformed_qfunc = commute_controlled(direction=direction)(qfunc) ops = qml.transforms.make_tape(transformed_qfunc)().operations names_expected = ["PauliX", "CNOT", "RX", "Toffoli"] wires_expected = [ Wires("a"), Wires(["a", "c"]), Wires("a"), Wires(["c", "a", "b"]) ] compare_operation_lists(ops, names_expected, wires_expected)
def test_dont_push_y_gates(self, direction): """Test that Y-basis gates next to controlled-Y-type gates on controls don't get pushed.""" def qfunc(): qml.CRY(-0.2, wires=["a", 2]) qml.PauliY(wires="a") qml.CNOT(wires=[1, 2]) qml.CY(wires=["a", 1]) qml.RY(0.3, wires="a") transformed_qfunc = commute_controlled()(qfunc) ops = qml.transforms.make_tape(transformed_qfunc)().operations names_expected = ["CRY", "PauliY", "CNOT", "CY", "RY"] wires_expected = [ Wires(["a", 2]), Wires("a"), Wires([1, 2]), Wires(["a", 1]), Wires("a") ] compare_operation_lists(ops, names_expected, wires_expected)
def test_push_y_gates_left(self): """Test that Y-basis gates after controlled-Y-type gates on targets get pushed behind.""" def qfunc(): qml.CRY(-0.5, wires=["a", 2]) qml.PauliY(wires=2) qml.CNOT(wires=[1, 2]) qml.CY(wires=["a", 1]) qml.RY(0.3, wires=1) transformed_qfunc = commute_controlled(direction="left")(qfunc) ops = qml.transforms.make_tape(transformed_qfunc)().operations names_expected = ["PauliY", "CRY", "CNOT", "RY", "CY"] wires_expected = [ Wires(2), Wires(["a", 2]), Wires([1, 2]), Wires(1), Wires(["a", 1]) ] compare_operation_lists(ops, names_expected, wires_expected)
dev = qml.device("default.qubit", wires=3) def qfunc(theta): qml.PauliX(wires=2) qml.S(wires=0) qml.CNOT(wires=[0, 1]) qml.PauliY(wires=1) qml.CRY(theta[0], wires=[2, 1]) qml.PhaseShift(theta[1], wires=0) qml.T(wires=0) qml.Toffoli(wires=[0, 1, 2]) return qml.expval(qml.PauliZ(0)) transformed_qfunc = commute_controlled()(qfunc) expected_op_list = [ "PauliX", "CNOT", "CRY", "PauliY", "Toffoli", "S", "PhaseShift", "T" ] expected_wires_list = [ Wires(2), Wires([0, 1]), Wires([2, 1]), Wires(1), Wires([0, 1, 2]), Wires(0), Wires(0), Wires(0), ]
Wires(1), Wires([1, 2]), Wires(2), Wires([2, 0]), ] * 2 compare_operation_lists(transformed_qnode.qtape.operations, names_expected, wires_expected) def qfunc(x, params): qml.templates.AngleEmbedding(x, wires=range(3)) qml.templates.BasicEntanglerLayers(params, wires=range(3)) return qml.expval(qml.PauliZ(wires=2)) pipeline = [commute_controlled(direction="left"), merge_rotations] transformed_qfunc = compile(pipeline=pipeline)(qfunc) dev = qml.device("default.qubit", wires=3) expected_op_list = ["RX"] * 3 + ["CNOT", "CNOT", "RX", "CNOT", "RX", "RX"] + ["CNOT"] * 3 expected_wires_list = [ Wires(0), Wires(1), Wires(2), Wires([0, 1]), Wires([1, 2]), Wires(0), Wires([2, 0]),