Skip to content

Commit

Permalink
Adds unitary testing for routed circuits (quantumlib#5846)
Browse files Browse the repository at this point in the history
Adds utilitiy functions that tests whether two circuits define the same unitary up to permutation of qubits.
  • Loading branch information
ammareltigani authored and rht committed May 1, 2023
1 parent 374462b commit 6f74fea
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 0 deletions.
1 change: 1 addition & 0 deletions cirq-core/cirq/testing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

from cirq.testing.circuit_compare import (
assert_circuits_with_terminal_measurements_are_equivalent,
assert_circuits_have_same_unitary_given_final_permutation,
assert_has_consistent_apply_unitary,
assert_has_consistent_apply_unitary_for_various_exponents,
assert_has_diagram,
Expand Down
34 changes: 34 additions & 0 deletions cirq-core/cirq/testing/circuit_compare.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,40 @@ def _first_differing_moment_index(
return None # coverage: ignore


def assert_circuits_have_same_unitary_given_final_permutation(
actual: circuits.AbstractCircuit,
expected: circuits.AbstractCircuit,
qubit_map: Dict[ops.Qid, ops.Qid],
) -> None:
"""Asserts two circuits have the same unitary up to a final permuation of qubits.
Args:
actual: A circuit computed by some code under test.
expected: The circuit that should have been computed.
qubit_map: the permutation of qubits from the beginning to the end of the circuit.
Raises:
ValueError: if 'qubit_map' is not a mapping from the qubits in 'actual' to themselves.
ValueError: if 'qubit_map' does not have the same set of keys and values.
"""
if set(qubit_map.keys()) != set(qubit_map.values()):
raise ValueError("'qubit_map' must have the same set of of keys and values.")

if not set(qubit_map.keys()).issubset(actual.all_qubits()):
raise ValueError(
f"'qubit_map' must be a mapping of the qubits in the circuit 'actual' to themselves."
)

actual_cp = actual.unfreeze()
initial_qubits, sorted_qubits = zip(*sorted(qubit_map.items(), key=lambda x: x[1]))
inverse_permutation = [sorted_qubits.index(q) for q in initial_qubits]
actual_cp.append(ops.QubitPermutationGate(list(inverse_permutation)).on(*sorted_qubits))

lin_alg_utils.assert_allclose_up_to_global_phase(
expected.unitary(), actual_cp.unitary(), atol=1e-8
)


def assert_has_diagram(
actual: Union[circuits.AbstractCircuit, circuits.Moment], desired: str, **kwargs
) -> None:
Expand Down
29 changes: 29 additions & 0 deletions cirq-core/cirq/testing/circuit_compare_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,35 @@ def test_assert_same_circuits():
)


def test_assert_circuits_have_same_unitary_given_final_permutation():
q = cirq.LineQubit.range(5)
expected = cirq.Circuit([cirq.Moment(cirq.CNOT(q[2], q[1]), cirq.CNOT(q[3], q[0]))])
actual = cirq.Circuit(
[
cirq.Moment(cirq.CNOT(q[2], q[1])),
cirq.Moment(cirq.SWAP(q[0], q[2])),
cirq.Moment(cirq.SWAP(q[0], q[1])),
cirq.Moment(cirq.CNOT(q[3], q[2])),
]
)
qubit_map = {q[0]: q[2], q[2]: q[1], q[1]: q[0]}
cirq.testing.assert_circuits_have_same_unitary_given_final_permutation(
actual, expected, qubit_map
)

qubit_map.update({q[2]: q[3]})
with pytest.raises(ValueError, match="'qubit_map' must have the same set"):
cirq.testing.assert_circuits_have_same_unitary_given_final_permutation(
actual, expected, qubit_map=qubit_map
)

bad_qubit_map = {q[0]: q[2], q[2]: q[4], q[4]: q[0]}
with pytest.raises(ValueError, match="'qubit_map' must be a mapping"):
cirq.testing.assert_circuits_have_same_unitary_given_final_permutation(
actual, expected, qubit_map=bad_qubit_map
)


def test_assert_has_diagram():
a, b = cirq.LineQubit.range(2)
circuit = cirq.Circuit(cirq.CNOT(a, b))
Expand Down

0 comments on commit 6f74fea

Please sign in to comment.