def test_compile_template(self): """Test that functions with templates are correctly expanded and compiled.""" # Push commuting gates to the right and merging rotations gives a circuit # with alternating RX and CNOT gates 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)) dev = qml.device("default.qubit", wires=3) qnode = qml.QNode(qfunc, dev) pipeline = [commute_controlled, merge_rotations] transformed_qfunc = compile(pipeline=pipeline)(qfunc) transformed_qnode = qml.QNode(transformed_qfunc, dev) x = np.array([0.1, 0.2, 0.3]) params = np.ones((2, 3)) original_result = qnode(x, params) transformed_result = transformed_qnode(x, params) assert np.allclose(original_result, transformed_result) names_expected = ["RX", "CNOT"] * 6 wires_expected = [ Wires(0), Wires([0, 1]), Wires(1), Wires([1, 2]), Wires(2), Wires([2, 0]), ] * 2 compare_operation_lists(transformed_qnode.qtape.operations, 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_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_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_default_pipeline(self, wires): """Test that the default pipeline returns the correct results.""" qfunc = build_qfunc(wires) dev = qml.device("default.qubit", wires=Wires(wires)) qnode = qml.QNode(qfunc, dev) transformed_qfunc = compile()(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", "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_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_compile_invalid_num_passes(self): """Test that error is raised for an invalid number of passes.""" qfunc = build_qfunc([0, 1, 2]) dev = qml.device("default.qubit", wires=[0, 1, 2]) transformed_qfunc = compile(num_passes=1.3)(qfunc) transformed_qnode = qml.QNode(transformed_qfunc, dev) with pytest.raises(ValueError, match="Number of passes must be an integer"): transformed_qnode(0.1, 0.2, 0.3)
def test_compile_invalid_pipeline(self): """Test that error is raised for an invalid function in the pipeline""" qfunc = build_qfunc([0, 1, 2]) dev = qml.device("default.qubit", wires=[0, 1, 2]) transformed_qfunc = compile(pipeline=[cancel_inverses, isinstance])(qfunc) transformed_qnode = qml.QNode(transformed_qfunc, dev) with pytest.raises(ValueError, match="Invalid transform function"): transformed_qnode(0.1, 0.2, 0.3)
def run_pipeline(self): pipeline = [ qml.transforms.single_tape_transform(DummyTransforms.merge_rotations), qml.transforms.single_tape_transform(DummyTransforms.commute_controlled), ] wires = [0, 1, 2] qfunc = build_qfunc(wires) dev = qml.device("default.qubit", wires=Wires(wires)) transformed_qfunc = compile(pipeline=pipeline, num_passes=num_passes)(qfunc) transformed_qnode = qml.QNode(transformed_qfunc, dev) transformed_result = transformed_qnode(0.3, 0.4, 0.5)
def test_compile_empty_pipeline(self, wires): """Test that an empty pipeline returns the original function.""" qfunc = build_qfunc(wires) dev = qml.device("default.qubit", wires=wires) qnode = qml.QNode(qfunc, dev) transformed_qfunc = compile(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 = [op.name for op in qnode.qtape.operations] wires_expected = [op.wires for op in qnode.qtape.operations] compare_operation_lists(transformed_qnode.qtape.operations, names_expected, wires_expected)
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]), Wires(1), Wires(2),