Skip to content

Commit

Permalink
Change qubit str representation (#5343)
Browse files Browse the repository at this point in the history
* Change qubit str representation

 - Changes grid qubit string representation from (x, y) to q(x, y)
 and line qubit from 1 to q1.
 - This will make the string representation more distinctive.
 - Added _circuit_diagram_info_ for Qid objects to make circuit diagrams
customizable for qubits.  This allows us to keep circuit diagrams
largely unchanged.
 - Other components such as pauli strings, that rely on qubit str
   representation for formatting are changed.

 This is a BREAKING CHANGE for anyone relying on string representation
 of qubits.

Fixes: #2405

* Fix a bunch of tests.

* A few more errors.

* Switch to q(0) and fix a bunch of tests.

* Formatting and more tests fixed.

* Fix more tests.

* Fix two last tests.

* Fix bb84 example.

* Fix contrib and notebook tests.

* Update cirq-core/cirq/sim/simulator_test.py

Co-authored-by: Matthew Neeley <mneeley@gmail.com>

* Update cirq-core/cirq/circuits/circuit.py

Co-authored-by: Matthew Neeley <mneeley@gmail.com>

* Revert Grid Device to use circuit diagram names instead.

Co-authored-by: Matthew Neeley <mneeley@gmail.com>
  • Loading branch information
dstrain115 and maffoo committed May 16, 2022
1 parent b1a5a39 commit 2d84676
Show file tree
Hide file tree
Showing 40 changed files with 314 additions and 262 deletions.
6 changes: 4 additions & 2 deletions cirq-core/cirq/circuits/circuit.py
Expand Up @@ -1187,7 +1187,7 @@ def to_text_diagram_drawer(
Args:
use_unicode_characters: Determines if unicode characters are
allowed (as opposed to ascii-only diagrams).
qubit_namer: Names qubits in diagram. Defaults to str.
qubit_namer: Names qubits in diagram. Defaults to using _circuit_diagram_info_ or str.
transpose: Arranges qubit wires vertically instead of horizontally.
include_tags: Whether to include tags in the operation.
draw_moment_groups: Whether to draw moment symbol or not
Expand All @@ -1210,7 +1210,9 @@ def to_text_diagram_drawer(
label_map = {labels[i]: i for i in range(len(labels))}

def default_namer(label_entity):
return str(label_entity) + ('' if transpose else ': ')
info = protocols.circuit_diagram_info(label_entity, default=None)
qubit_name = info.wire_symbols[0] if info else str(label_entity)
return qubit_name + ('' if transpose else ': ')

if qubit_namer is None:
qubit_namer = default_namer
Expand Down
4 changes: 2 additions & 2 deletions cirq-core/cirq/circuits/circuit_operation_test.py
Expand Up @@ -577,7 +577,7 @@ def test_string_format():
== f"""\
[ 0: ───X───X─── ]
[ │ ]
[ 1: ───H───@─── ](qubit_map={{1: 2}}, parent_path=('outer', 'inner'),\
[ 1: ───H───@─── ](qubit_map={{q(1): q(2)}}, parent_path=('outer', 'inner'),\
repetition_ids=['a', 'b', 'c'])"""
)
assert (
Expand Down Expand Up @@ -611,7 +611,7 @@ def test_string_format():
assert (
str(op3)
== f"""\
[ 0: ───X^b───M('m')─── ](qubit_map={{0: 1}}, \
[ 0: ───X^b───M('m')─── ](qubit_map={{q(0): q(1)}}, \
key_map={{m: p}}, params={{b: 2}})"""
)
assert (
Expand Down
2 changes: 1 addition & 1 deletion cirq-core/cirq/circuits/qasm_output_test.py
Expand Up @@ -563,7 +563,7 @@ def test_reset():
include "qelib1.inc";
// Qubits: [0, 1]
// Qubits: [q(0), q(1)]
qreg q[2];
Expand Down
18 changes: 9 additions & 9 deletions cirq-core/cirq/contrib/qcircuit/qcircuit_test.py
Expand Up @@ -118,9 +118,9 @@ def test_other_diagram():
expected_diagram = r"""
\Qcircuit @R=1em @C=0.75em {
\\
&\lstick{\text{0}}& \qw&\targ \qw&\qw\\
&\lstick{\text{1}}& \qw&\gate{\text{Y}} \qw&\qw\\
&\lstick{\text{2}}& \qw&\gate{\text{Z}} \qw&\qw\\
&\lstick{\text{q(0)}}& \qw&\targ \qw&\qw\\
&\lstick{\text{q(1)}}& \qw&\gate{\text{Y}} \qw&\qw\\
&\lstick{\text{q(2)}}& \qw&\gate{\text{Z}} \qw&\qw\\
\\
}""".strip()
assert_has_qcircuit_diagram(circuit, expected_diagram)
Expand Down Expand Up @@ -148,10 +148,10 @@ def test_two_cx_diagram():
expected_diagram = r"""
\Qcircuit @R=1em @C=0.75em {
\\
&\lstick{\text{0}}& \qw&\control \qw & \qw &\control \qw & \qw &\qw\\
&\lstick{\text{1}}& \qw& \qw\qwx&\control \qw & \qw\qwx&\control \qw &\qw\\
&\lstick{\text{2}}& \qw&\targ \qw\qwx& \qw\qwx&\targ \qw\qwx& \qw\qwx&\qw\\
&\lstick{\text{3}}& \qw& \qw &\targ \qw\qwx& \qw &\targ \qw\qwx&\qw\\
&\lstick{\text{q(0)}}& \qw&\control \qw & \qw &\control \qw & \qw &\qw\\
&\lstick{\text{q(1)}}& \qw& \qw\qwx&\control \qw & \qw\qwx&\control \qw &\qw\\
&\lstick{\text{q(2)}}& \qw&\targ \qw\qwx& \qw\qwx&\targ \qw\qwx& \qw\qwx&\qw\\
&\lstick{\text{q(3)}}& \qw& \qw &\targ \qw\qwx& \qw &\targ \qw\qwx&\qw\\
\\
}""".strip()
assert_has_qcircuit_diagram(circuit, expected_diagram)
Expand All @@ -164,8 +164,8 @@ def test_sqrt_iswap_diagram():
expected_diagram = r"""
\Qcircuit @R=1em @C=0.75em {
\\
&\lstick{\text{0}}& \qw&\multigate{1}{\text{ISWAP}^{0.5}} \qw&\qw\\
&\lstick{\text{1}}& \qw&\ghost{\text{ISWAP}^{0.5}} \qw&\qw\\
&\lstick{\text{q(0)}}& \qw&\multigate{1}{\text{ISWAP}^{0.5}} \qw&\qw\\
&\lstick{\text{q(1)}}& \qw&\ghost{\text{ISWAP}^{0.5}} \qw&\qw\\
\\
}""".strip()
assert_has_qcircuit_diagram(circuit, expected_diagram)
8 changes: 4 additions & 4 deletions cirq-core/cirq/contrib/quantum_volume/quantum_volume_test.py
Expand Up @@ -97,10 +97,10 @@ def test_sample_heavy_set_with_parity():
result = cirq.ResultDict(
params=cirq.ParamResolver({}),
measurements={
'0': np.array([[1], [0]]),
'1': np.array([[0], [1]]),
'2': np.array([[1], [1]]),
'3': np.array([[0], [0]]),
'q(0)': np.array([[1], [0]]),
'q(1)': np.array([[0], [1]]),
'q(2)': np.array([[1], [1]]),
'q(3)': np.array([[0], [0]]),
},
)
sampler.run = MagicMock(return_value=result)
Expand Down
18 changes: 9 additions & 9 deletions cirq-core/cirq/contrib/quimb/mps_simulator_test.py
Expand Up @@ -214,19 +214,19 @@ def test_measurement_1qubit():
simulator = ccq.mps_simulator.MPSSimulator()

result = simulator.run(circuit, repetitions=100)
assert sum(result.measurements['1'])[0] < 80
assert sum(result.measurements['1'])[0] > 20
assert sum(result.measurements['q(1)'])[0] < 80
assert sum(result.measurements['q(1)'])[0] > 20


def test_reset():
q = cirq.LineQubit(0)
simulator = ccq.mps_simulator.MPSSimulator()
c = cirq.Circuit(cirq.X(q), cirq.reset(q), cirq.measure(q))
assert simulator.sample(c)['0'][0] == 0
assert simulator.sample(c)['q(0)'][0] == 0
c = cirq.Circuit(cirq.H(q), cirq.reset(q), cirq.measure(q))
assert simulator.sample(c)['0'][0] == 0
assert simulator.sample(c)['q(0)'][0] == 0
c = cirq.Circuit(cirq.reset(q), cirq.measure(q))
assert simulator.sample(c)['0'][0] == 0
assert simulator.sample(c)['q(0)'][0] == 0


def test_measurement_2qubits():
Expand All @@ -236,7 +236,7 @@ def test_measurement_2qubits():
simulator = ccq.mps_simulator.MPSSimulator()

repetitions = 1024
measurement = simulator.run(circuit, repetitions=repetitions).measurements['0,2']
measurement = simulator.run(circuit, repetitions=repetitions).measurements['q(0),q(2)']

result_counts = {'00': 0, '01': 0, '10': 0, '11': 0}
for i in range(repetitions):
Expand Down Expand Up @@ -309,7 +309,7 @@ def test_empty_step_result():
step_result = next(sim.simulate_moment_steps(cirq.Circuit(cirq.measure(q0))))
assert (
str(step_result)
== """0=0
== """q(0)=0
TensorNetwork([
Tensor(shape=(2,), inds=('i_0',), tags=set()),
])"""
Expand All @@ -322,7 +322,7 @@ def test_step_result_repr_pretty():
step_result = next(sim.simulate_moment_steps(cirq.Circuit(cirq.measure(q0))))
cirq.testing.assert_repr_pretty(
step_result,
"""0=0
"""q(0)=0
TensorNetwork([
Tensor(shape=(2,), inds=('i_0',), tags=set()),
])""",
Expand Down Expand Up @@ -441,7 +441,7 @@ def test_run_no_repetitions():
simulator = ccq.mps_simulator.MPSSimulator()
circuit = cirq.Circuit(cirq.H(q0), cirq.measure(q0))
result = simulator.run(circuit, repetitions=0)
assert len(result.measurements['0']) == 0
assert len(result.measurements['q(0)']) == 0


def test_run_parameters_not_resolved():
Expand Down
19 changes: 10 additions & 9 deletions cirq-core/cirq/contrib/routing/swap_network_test.py
Expand Up @@ -70,14 +70,15 @@ def test_swap_network_str():
swap_network = ccr.SwapNetwork(routed_circuit, initial_mapping)
actual_str = str(swap_network)
expected_str = """
(0, 0): ───0───ZZ───0───╲0╱───1────────1─────────1───ZZ───1───╲0╱───3────────3─────────3───ZZ───3───╲0╱───4───
│ │ │ │
(1, 0): ───1───ZZ───1───╱1╲───0───ZZ───0───╲0╱───3───ZZ───3───╱1╲───1───ZZ───1───╲0╱───4───ZZ───4───╱1╲───3───
│ │
(2, 0): ───2───ZZ───2───╲0╱───3───ZZ───3───╱1╲───0───ZZ───0───╲0╱───4───ZZ───4───╱1╲───1───ZZ───1───╲0╱───2───
│ │ │ │
(3, 0): ───3───ZZ───3───╱1╲───2───ZZ───2───╲0╱───4───ZZ───4───╱1╲───0───ZZ───0───╲0╱───2───ZZ───2───╱1╲───1───
│ │
(4, 0): ───4────────4─────────4───ZZ───4───╱1╲───2────────2─────────2───ZZ───2───╱1╲───0────────0─────────0───
(0, 0): ───q(0)───ZZ───q(0)───╲0╱───q(1)────────q(1)─────────q(1)───ZZ───q(1)───╲0╱───q(3)────────q(3)─────────q(3)───ZZ───q(3)───╲0╱───q(4)───
(1, 0): ───q(1)───ZZ───q(1)───╱1╲───q(0)───ZZ───q(0)───╲0╱───q(3)───ZZ───q(3)───╱1╲───q(1)───ZZ───q(1)───╲0╱───q(4)───ZZ───q(4)───╱1╲───q(3)───
(2, 0): ───q(2)───ZZ───q(2)───╲0╱───q(3)───ZZ───q(3)───╱1╲───q(0)───ZZ───q(0)───╲0╱───q(4)───ZZ───q(4)───╱1╲───q(1)───ZZ───q(1)───╲0╱───q(2)───
(3, 0): ───q(3)───ZZ───q(3)───╱1╲───q(2)───ZZ───q(2)───╲0╱───q(4)───ZZ───q(4)───╱1╲───q(0)───ZZ───q(0)───╲0╱───q(2)───ZZ───q(2)───╱1╲───q(1)───
(4, 0): ───q(4)────────q(4)─────────q(4)───ZZ───q(4)───╱1╲───q(2)────────q(2)─────────q(2)───ZZ───q(2)───╱1╲───q(0)────────q(0)─────────q(0)───
""".strip()
print(actual_str)
assert actual_str == expected_str
16 changes: 14 additions & 2 deletions cirq-core/cirq/devices/grid_qubit.py
Expand Up @@ -254,7 +254,14 @@ def __repr__(self) -> str:
return f"cirq.GridQid({self.row}, {self.col}, dimension={self.dimension})"

def __str__(self) -> str:
return f"({self.row}, {self.col}) (d={self.dimension})"
return f"q({self.row}, {self.col}) (d={self.dimension})"

def _circuit_diagram_info_(
self, args: 'cirq.CircuitDiagramInfoArgs'
) -> 'cirq.CircuitDiagramInfo':
return protocols.CircuitDiagramInfo(
wire_symbols=(f"({self.row}, {self.col}) (d={self.dimension})",)
)

def _json_dict_(self) -> Dict[str, Any]:
return protocols.obj_to_dict_helper(self, ['row', 'col', 'dimension'])
Expand Down Expand Up @@ -403,7 +410,12 @@ def __repr__(self) -> str:
return f"cirq.GridQubit({self.row}, {self.col})"

def __str__(self) -> str:
return f"({self.row}, {self.col})"
return f"q({self.row}, {self.col})"

def _circuit_diagram_info_(
self, args: 'cirq.CircuitDiagramInfoArgs'
) -> 'cirq.CircuitDiagramInfo':
return protocols.CircuitDiagramInfo(wire_symbols=(f"({self.row}, {self.col})",))

def _json_dict_(self) -> Dict[str, Any]:
return protocols.obj_to_dict_helper(self, ['row', 'col'])
Expand Down
13 changes: 11 additions & 2 deletions cirq-core/cirq/devices/grid_qubit_test.py
Expand Up @@ -53,8 +53,17 @@ def test_pickled_hash():


def test_str():
assert str(cirq.GridQubit(5, 2)) == '(5, 2)'
assert str(cirq.GridQid(5, 2, dimension=3)) == '(5, 2) (d=3)'
assert str(cirq.GridQubit(5, 2)) == 'q(5, 2)'
assert str(cirq.GridQid(5, 2, dimension=3)) == 'q(5, 2) (d=3)'


def test_circuit_info():
assert cirq.circuit_diagram_info(cirq.GridQubit(5, 2)) == cirq.CircuitDiagramInfo(
wire_symbols=('(5, 2)',)
)
assert cirq.circuit_diagram_info(cirq.GridQid(5, 2, dimension=3)) == cirq.CircuitDiagramInfo(
wire_symbols=('(5, 2) (d=3)',)
)


def test_repr():
Expand Down
14 changes: 12 additions & 2 deletions cirq-core/cirq/devices/line_qubit.py
Expand Up @@ -190,7 +190,12 @@ def __repr__(self) -> str:
return f"cirq.LineQid({self.x}, dimension={self.dimension})"

def __str__(self) -> str:
return f"{self.x} (d={self.dimension})"
return f"q({self.x}) (d={self.dimension})"

def _circuit_diagram_info_(
self, args: 'cirq.CircuitDiagramInfoArgs'
) -> 'cirq.CircuitDiagramInfo':
return protocols.CircuitDiagramInfo(wire_symbols=(f"{self.x} (d={self.dimension})",))

def _json_dict_(self) -> Dict[str, Any]:
return protocols.obj_to_dict_helper(self, ['x', 'dimension'])
Expand Down Expand Up @@ -241,7 +246,12 @@ def __repr__(self) -> str:
return f"cirq.LineQubit({self.x})"

def __str__(self) -> str:
return f"{self.x}"
return f"q({self.x})"

def _circuit_diagram_info_(
self, args: 'cirq.CircuitDiagramInfoArgs'
) -> 'cirq.CircuitDiagramInfo':
return protocols.CircuitDiagramInfo(wire_symbols=(f"{self.x}",))

def _json_dict_(self) -> Dict[str, Any]:
return protocols.obj_to_dict_helper(self, ['x'])
4 changes: 2 additions & 2 deletions cirq-core/cirq/devices/line_qubit_test.py
Expand Up @@ -35,8 +35,8 @@ def test_eq():


def test_str():
assert str(cirq.LineQubit(5)) == '5'
assert str(cirq.LineQid(5, dimension=3)) == '5 (d=3)'
assert str(cirq.LineQubit(5)) == 'q(5)'
assert str(cirq.LineQid(5, dimension=3)) == 'q(5) (d=3)'


def test_repr():
Expand Down
4 changes: 2 additions & 2 deletions cirq-core/cirq/ion/ion_device_test.py
Expand Up @@ -202,11 +202,11 @@ def test_validate_circuit_repeat_measurement_keys():


def test_ion_device_str():
assert str(ion_device(3)) == "0───1───2"
assert str(ion_device(3)) == "q(0)───q(1)───q(2)"


def test_ion_device_pretty_repr():
cirq.testing.assert_repr_pretty(ion_device(3), "0───1───2")
cirq.testing.assert_repr_pretty(ion_device(3), "q(0)───q(1)───q(2)")
cirq.testing.assert_repr_pretty(ion_device(3), "IonDevice(...)", cycle=True)


Expand Down
16 changes: 8 additions & 8 deletions cirq-core/cirq/neutral_atoms/neutral_atom_devices_test.py
Expand Up @@ -276,10 +276,10 @@ def test_str():
assert (
str(square_device(2, 2)).strip()
== """
(0, 0)───(0, 1)
│ │
│ │
(1, 0)───(1, 1)
q(0, 0)───q(0, 1)
q(1, 0)───q(1, 1)
""".strip()
)

Expand All @@ -288,10 +288,10 @@ def test_repr_pretty():
cirq.testing.assert_repr_pretty(
square_device(2, 2),
"""
(0, 0)───(0, 1)
│ │
│ │
(1, 0)───(1, 1)
q(0, 0)───q(0, 1)
q(1, 0)───q(1, 1)
""".strip(),
)
cirq.testing.assert_repr_pretty(square_device(2, 2), "cirq.NeutralAtomDevice(...)", cycle=True)
Expand Down
4 changes: 2 additions & 2 deletions cirq-core/cirq/ops/classically_controlled_operation_test.py
Expand Up @@ -208,7 +208,7 @@ def test_qasm():
include "qelib1.inc";
// Qubits: [0, 1]
// Qubits: [q(0), q(1)]
qreg q[2];
creg m_a[1];
Expand Down Expand Up @@ -416,7 +416,7 @@ def test_decompose():
def test_str():
q0 = cirq.LineQubit(0)
op = cirq.X(q0).with_classical_controls('a')
assert str(op) == 'X(0).with_classical_controls(a)'
assert str(op) == 'X(q(0)).with_classical_controls(a)'


def test_scope_local():
Expand Down
14 changes: 7 additions & 7 deletions cirq-core/cirq/ops/controlled_gate_test.py
Expand Up @@ -514,11 +514,11 @@ def test_circuit_diagram():
cirq.testing.assert_has_diagram(
c,
"""
0: ───@──────
0: ───@─────────
1: ───H(1)───
1: ───H(q(1))───
2: ───H(2)───
2: ───H(q(2))───
""",
)

Expand All @@ -530,13 +530,13 @@ def test_circuit_diagram():
cirq.testing.assert_has_diagram(
c,
"""
0 (d=3): ───@────────────
0 (d=3): ───@───────────────
1 (d=3): ───(0,1)────────
1 (d=3): ───(0,1)───────────
2 (d=3): ───(0,2)────────
2 (d=3): ───(0,2)───────────
3 (d=2): ───H(3 (d=2))───
3 (d=2): ───H(q(3) (d=2))───
""",
)

Expand Down

0 comments on commit 2d84676

Please sign in to comment.