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_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_two_qubits_rotation_merge_tolerance(self): """Test whether tolerance argument is respected for merging.""" def qfunc(): qml.RZ(1e-7, wires=0) qml.RZ(-2e-7, wires=0) # Try with default tolerance; these ops should still be applied transformed_qfunc = merge_rotations()(qfunc) ops = qml.transforms.make_tape(transformed_qfunc)().operations assert len(ops) == 1 assert ops[0].name == "RZ" assert ops[0].parameters[0] == -1e-7 # Now try with higher tolerance threshold; the ops should cancel transformed_qfunc = merge_rotations(atol=1e-5)(qfunc) ops = qml.transforms.make_tape(transformed_qfunc)().operations assert len(ops) == 0
def test_controlled_rotation_merge(self, theta_1, theta_2, expected_ops): """Test that adjacent controlled rotations on the same wires in same order get merged.""" def qfunc(): qml.CRY(theta_1, wires=["w1", "w2"]) qml.CRY(theta_2, wires=["w1", "w2"]) transformed_qfunc = merge_rotations()(qfunc) ops = qml.transforms.make_tape(transformed_qfunc)().operations assert len(ops) == len(expected_ops) # Check that all operations and parameter values are as expected for op_obtained, op_expected in zip(ops, expected_ops): assert op_obtained.name == op_expected.name assert np.allclose(op_obtained.parameters, op_expected.parameters)
def test_controlled_rotation_no_merge(self): """Test that adjacent controlled rotations on the same wires in different order don't merge.""" def qfunc(): qml.CRX(0.2, wires=["w1", "w2"]) qml.CRX(0.3, wires=["w2", "w1"]) transformed_qfunc = merge_rotations()(qfunc) ops = qml.transforms.make_tape(transformed_qfunc)().operations names_expected = ["CRX", "CRX"] wires_expected = [Wires(["w1", "w2"]), Wires(["w2", "w1"])] compare_operation_lists(ops, names_expected, wires_expected) assert ops[0].parameters[0] == 0.2 assert ops[1].parameters[0] == 0.3
def test_two_qubits_rotation_no_merge(self, theta_1, theta_2, expected_ops): """Test that a two-qubit circuit with rotations on different qubits do not get merged.""" def qfunc(): qml.RZ(theta_1, wires=0) qml.RZ(theta_2, wires=1) transformed_qfunc = merge_rotations()(qfunc) ops = qml.transforms.make_tape(transformed_qfunc)().operations assert len(ops) == len(expected_ops) for op_obtained, op_expected in zip(ops, expected_ops): assert op_obtained.name == op_expected.name assert np.allclose(op_obtained.parameters, op_expected.parameters)
def test_one_qubit_rotation_merge(self, theta_1, theta_2, expected_ops): """Test that a single-qubit circuit with adjacent rotation along the same axis either merge, or cancel if the angles sum to 0.""" def qfunc(): qml.RZ(theta_1, wires=0) qml.RZ(theta_2, wires=0) transformed_qfunc = merge_rotations()(qfunc) ops = qml.transforms.make_tape(transformed_qfunc)().operations assert len(ops) == len(expected_ops) # Check that all operations and parameter values are as expected for op_obtained, op_expected in zip(ops, expected_ops): assert op_obtained.name == op_expected.name assert np.allclose(op_obtained.parameters, op_expected.parameters)
def test_one_qubit_rotation_blocked(self): """Test that rotations on one-qubit separated by a "blocking" operation don't merge.""" def qfunc(): qml.RX(0.5, wires=0) qml.Hadamard(wires=0) qml.RX(0.4, wires=0) transformed_qfunc = merge_rotations()(qfunc) ops = qml.transforms.make_tape(transformed_qfunc)().operations names_expected = ["RX", "Hadamard", "RX"] wires_expected = [Wires(0)] * 3 compare_operation_lists(ops, names_expected, wires_expected) assert ops[0].parameters[0] == 0.5 assert ops[2].parameters[0] == 0.4
def test_two_qubits_rotation_blocked(self): """Test that rotations on a two-qubit system separated by a "blocking" operation don't merge.""" def qfunc(): qml.RX(-0.42, wires=0) qml.CNOT(wires=[0, 1]) qml.RX(0.8, wires=0) transformed_qfunc = merge_rotations()(qfunc) ops = qml.transforms.make_tape(transformed_qfunc)().operations names_expected = ["RX", "CNOT", "RX"] wires_expected = [Wires(0), Wires([0, 1]), Wires(0)] compare_operation_lists(ops, names_expected, wires_expected) assert ops[0].parameters[0] == -0.42 assert ops[2].parameters[0] == 0.8
def test_two_qubits_merge_with_adjoint(self, theta_11, theta_12, theta_21, theta_22, expected_ops): """Test that adjoint rotations on different qubits get merged.""" def qfunc(): qml.CRX(theta_11, wires=[0, 1]) qml.adjoint(qml.RY)(theta_21, wires=2) qml.adjoint(qml.CRX)(theta_12, wires=[0, 1]) qml.RY(theta_22, wires=2) transformed_qfunc = merge_rotations()(qfunc) ops = qml.transforms.make_tape(transformed_qfunc)().operations assert len(ops) == len(expected_ops) for op_obtained, op_expected in zip(ops, expected_ops): assert op_obtained.name == op_expected.name assert np.allclose(op_obtained.parameters, op_expected.parameters)
def test_two_qubits_merge_gate_subset(self): """Test that specifying a subset of operations to include merges correctly.""" def qfunc(): qml.CRX(0.1, wires=[0, 1]) qml.CRX(0.2, wires=[0, 1]) qml.RY(0.3, wires=["a"]) qml.RY(0.5, wires=["a"]) qml.RX(-0.5, wires=[2]) qml.RX(0.2, wires=[2]) transformed_qfunc = merge_rotations(include_gates=["RX", "CRX"])(qfunc) ops = qml.transforms.make_tape(transformed_qfunc)().operations names_expected = ["CRX", "RY", "RY", "RX"] wires_expected = [Wires([0, 1]), Wires("a"), Wires("a"), Wires(2)] compare_operation_lists(ops, names_expected, wires_expected) assert qml.math.isclose(ops[0].parameters[0], 0.3) assert qml.math.isclose(ops[1].parameters[0], 0.3) assert qml.math.isclose(ops[2].parameters[0], 0.5) assert qml.math.isclose(ops[3].parameters[0], -0.3)
def qfunc(theta): qml.Hadamard(wires=0) qml.RZ(theta[0], wires=0) qml.PauliY(wires=1) qml.RZ(theta[1], wires=0) qml.CNOT(wires=[1, 2]) qml.CRY(theta[2], wires=[1, 2]) qml.PauliZ(wires=0) qml.CRY(theta[3], wires=[1, 2]) qml.Rot(theta[0], theta[1], theta[2], wires=1) qml.Rot(theta[2], theta[3], theta[0], wires=1) qml.Rot(0.0, 0.0, 0.0, wires=1) return qml.expval(qml.PauliX(0) @ qml.PauliX(2)) transformed_qfunc = merge_rotations()(qfunc) expected_op_list = ["Hadamard", "RZ", "PauliY", "CNOT", "CRY", "PauliZ", "Rot"] expected_wires_list = [ Wires(0), Wires(0), Wires(1), Wires([1, 2]), Wires([1, 2]), Wires(0), Wires(1), ] class TestMergeRotationsInterfaces: """Test that rotation merging works in all interfaces."""