Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds unitary testing for routed circuits #5846

Merged
merged 8 commits into from
Sep 2, 2022
Next Next commit
added unitary testing for routed circuits
  • Loading branch information
ammareltigani committed Aug 30, 2022
commit 32641ad12995a6fb58f3f666eb9b1716402534bd
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_up_to_permutation,
assert_has_consistent_apply_unitary,
assert_has_consistent_apply_unitary_for_various_exponents,
assert_has_diagram,
Expand Down
42 changes: 42 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,48 @@ def _first_differing_moment_index(
return None # coverage: ignore


def assert_circuits_have_same_unitary_up_to_permutation(
actual: circuits.Circuit,
expected: circuits.Circuit,
imap: Optional[Dict[ops.Qid, ops.Qid]] = None,
fmap: Optional[Dict[ops.Qid, ops.Qid]] = None,
) -> None:
"""Asserts two circuits have the same unitary up to an initial and final permuation of qubits.

Args:
actual: A circuit computed by some code under test.
expected: The circuit that should have been computed.
imap: the permutation of qubits at the beginning of the actual circuit.
fmap: the permutation of qubits at the end of the actual circuit.

Raises:
ValueError: if the initial and final permuations map to different qubits.
"""

if (
(imap is None and fmap is not None)
or (fmap is None and imap is not None)
or (imap is not None and fmap is not None and set(fmap.values()) != set(imap.values()))
):
raise ValueError("The initial and final permuation must map to the same qubits.")

if imap is not None and fmap is not None:
inverse_fmap = {v: k for k, v in fmap.items()}
fmap_to_imap = {k: imap[inverse_fmap[k]] for k in inverse_fmap}
device_qubits, permutation_qubits = zip(*sorted(fmap_to_imap.items(), key=lambda x: x[0]))
permutation = [device_qubits.index(q) for q in permutation_qubits]
actual.append(ops.QubitPermutationGate(list(permutation)).on(*device_qubits))

_, initial_order = zip(*sorted(imap.items(), key=lambda x: x[0]))
lin_alg_utils.assert_allclose_up_to_global_phase(
expected.unitary(), actual.unitary(qubit_order=initial_order), atol=1e-8
)
else:
lin_alg_utils.assert_allclose_up_to_global_phase(
expected.unitary(), actual.unitary(), atol=1e-8
)


def assert_has_diagram(
actual: Union[circuits.AbstractCircuit, circuits.Moment], desired: str, **kwargs
) -> None:
Expand Down
60 changes: 60 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,66 @@ def test_assert_same_circuits():
)


def test_assert_circuits_have_same_unitary_up_to_permutation():
expected = cirq.Circuit(
[
cirq.Moment(cirq.CNOT(cirq.NamedQubit('3'), cirq.NamedQubit('2'))),
cirq.Moment(
cirq.CNOT(cirq.NamedQubit('0'), cirq.NamedQubit('2')), cirq.X(cirq.NamedQubit('3'))
),
cirq.Moment(
cirq.CNOT(cirq.NamedQubit('3'), cirq.NamedQubit('0')),
cirq.CNOT(cirq.NamedQubit('2'), cirq.NamedQubit('1')),
),
]
)
actual = cirq.Circuit(
[
cirq.Moment(cirq.CNOT(cirq.GridQubit(3, 4), cirq.GridQubit(4, 4))),
cirq.Moment(
cirq.X(cirq.GridQubit(3, 4)), cirq.CNOT(cirq.GridQubit(5, 4), cirq.GridQubit(4, 4))
),
cirq.Moment(cirq.CNOT(cirq.GridQubit(4, 4), cirq.GridQubit(4, 3))),
cirq.Moment(cirq.SWAP(cirq.GridQubit(5, 4), cirq.GridQubit(4, 4))),
cirq.Moment(cirq.CNOT(cirq.GridQubit(3, 4), cirq.GridQubit(4, 4))),
]
)

imap = {
cirq.NamedQubit('3'): cirq.GridQubit(3, 4),
cirq.NamedQubit('2'): cirq.GridQubit(4, 4),
cirq.NamedQubit('0'): cirq.GridQubit(5, 4),
cirq.NamedQubit('1'): cirq.GridQubit(4, 3),
}
fmap = {
cirq.NamedQubit('3'): cirq.GridQubit(3, 4),
cirq.NamedQubit('2'): cirq.GridQubit(5, 4),
cirq.NamedQubit('0'): cirq.GridQubit(4, 4),
cirq.NamedQubit('1'): cirq.GridQubit(4, 3),
}
cirq.testing.assert_circuits_have_same_unitary_up_to_permutation(
actual, expected, imap=imap, fmap=fmap
)
cirq.testing.assert_circuits_have_same_unitary_up_to_permutation(actual, actual)

with pytest.raises(ValueError):
cirq.testing.assert_circuits_have_same_unitary_up_to_permutation(
actual, expected, imap=imap
)
bad_fmap = {
cirq.NamedQubit('3'): cirq.GridQubit(3, 4),
cirq.NamedQubit('2'): cirq.GridQubit(5, 4),
cirq.NamedQubit('0'): cirq.GridQubit(4, 4),
cirq.NamedQubit('1'): cirq.GridQubit(8, 3),
}
with pytest.raises(
ValueError, match="The initial and final permuation must map to the same qubits."
):
cirq.testing.assert_circuits_have_same_unitary_up_to_permutation(
actual, expected, imap=imap, fmap=bad_fmap
)


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