Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions qiskit/circuit/controlledgate.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,20 @@ def __init__(
self.ctrl_state = ctrl_state
self._name = name

def __repr__(self) -> str:
"""Return a representation of the ControlledGate.

Returns:
A string representation showing the gate's class name, name, label (if set),
number of control qubits, control state, and parameters.
"""
label_str = f" labeled '{self._label}'" if self._label is not None else ""
return (
f"<{self.__class__.__name__} '{self._name}'{label_str} with "
f"{self._num_ctrl_qubits} control qubits, control state = {self._ctrl_state} "
f"and params={self.params}>"
)

@property
def definition(self) -> QuantumCircuit:
"""Return definition in terms of other basic gates. If the gate has
Expand Down
13 changes: 13 additions & 0 deletions qiskit/circuit/gate.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,19 @@ def __init__(
# Set higher priority than Numpy array and matrix classes
__array_priority__ = 20

def __repr__(self) -> str:
"""Return a representation of the Gate.

Returns:
A string representation showing the gate's class name, name, label (if set),
number of qubits, number of classical bits, and parameters.
"""
label_str = f" labeled '{self.label}'" if self.label is not None else ""
return (
f"<{self.__class__.__name__} '{self.name}'{label_str} with "
f"{self.num_qubits} qubits, {self.num_clbits} clbits and params={self.params}>"
)

def to_matrix(self) -> np.ndarray:
"""Return a Numpy.array for the gate unitary matrix.

Expand Down
13 changes: 13 additions & 0 deletions qiskit/circuit/quantumcircuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -1559,6 +1559,19 @@ def metadata(self, metadata: dict):
raise TypeError("Only a dictionary is accepted for circuit metadata")
self._metadata = metadata

def __repr__(self) -> str:
"""Return a representation of the QuantumCircuit.

Returns:
A string representation showing the circuit's name, number of qubits,
number of classical bits, number of instructions, and global phase.
"""
return (
f"<QuantumCircuit '{self.name}' with "
f"{self.num_qubits} qubits, {self.num_clbits} clbits, "
f"{len(self._data)} instructions, and global_phase={self.global_phase}>"
)

def __str__(self) -> str:
return str(self.draw(output="text"))

Expand Down
11 changes: 11 additions & 0 deletions qiskit/primitives/statevector_estimator.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,17 @@ def __init__(
self._default_precision = default_precision
self._seed = seed

def __repr__(self) -> str:
"""Return a representation of the StatevectorEstimator.

Returns:
A string representation showing the default precision and seed information.
"""
return (
f"<{self.__class__.__name__} with "
f"default_precision={self._default_precision}, seed={self._seed}>"
)

@property
def default_precision(self) -> float:
"""Return the default precision"""
Expand Down
11 changes: 11 additions & 0 deletions qiskit/primitives/statevector_sampler.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,17 @@ def __init__(self, *, default_shots: int = 1024, seed: np.random.Generator | int
self._default_shots = default_shots
self._seed = seed

def __repr__(self) -> str:
"""Return a representation of the StatevectorSampler.

Returns:
A string representation showing the default shots and seed information.
"""
return (
f"<{self.__class__.__name__} with "
f"default_shots={self._default_shots}, seed={self._seed}>"
)

@property
def default_shots(self) -> int:
"""Return the default shots"""
Expand Down
27 changes: 27 additions & 0 deletions qiskit/transpiler/passmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,33 @@ def __init__(
max_iteration=max_iteration,
)

def __repr__(self) -> str:
"""Return a representation of the PassManager.

Returns:
A string representation showing the number of pass sets, total passes,
max iterations, and property information.
"""
if self.property_set is None or len(self.property_set) == 0:
properties = "0 properties"
else:
prop_keys = list(self.property_set.keys())
nprops = len(prop_keys)
if nprops > 3:
_plist = "', '".join(prop_keys[0:3]) + "', ..."
else:
_plist = "', '".join(prop_keys) + "'"
properties = f"{nprops} properties ('{_plist})"

# _tasks is a list of lists of Task objects
npass = sum(len(task_list) for task_list in self._tasks)

return (
f"<{type(self).__name__} with "
f"{len(self._tasks)} sets, {npass} passes, "
f"{self.max_iteration} max iterations, and {properties}>"
)

def _passmanager_frontend(
self,
input_program: QuantumCircuit,
Expand Down
73 changes: 73 additions & 0 deletions releasenotes/notes/add-repr-methods-a9676b9f3162bd4a.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
---
features_circuits:
- |
Added ``__repr__`` methods to :class:`~qiskit.circuit.Gate` and
:class:`~qiskit.circuit.ControlledGate` to provide more informative
string representations when inspecting gate objects in interactive
environments.

The :class:`~qiskit.circuit.Gate` representation shows the gate name,
label (if set), number of qubits/clbits, and parameters::

from qiskit.circuit.library import HGate

h_gate = HGate(label='my_hadamard')
print(repr(h_gate))
# Output: <HGate 'h' labeled 'my_hadamard' with 1 qubits, 0 clbits and params=[]>

The :class:`~qiskit.circuit.ControlledGate` representation shows the gate
name, label (if set), number of control qubits, control state, and
parameters::

from qiskit.circuit.library import CXGate

cx_gate = CXGate(label='my_cnot')
print(repr(cx_gate))
# Output: <CXGate 'cx' labeled 'my_cnot' with 1 control qubits, control state = 1 and params=[]>

Added ``__repr__`` method to :class:`~qiskit.circuit.QuantumCircuit` to
provide a concise summary of the circuit::

from qiskit.circuit import QuantumCircuit

qc = QuantumCircuit(2, 2, name='Bell')
qc.h(0)
qc.cx(0, 1)
print(repr(qc))
# Output: <QuantumCircuit 'Bell' with 2 qubits, 2 clbits, 2 instructions, and global_phase=0.0>

features_primitives:
- |
Added ``__repr__`` methods to :class:`~qiskit.primitives.StatevectorSampler`
and :class:`~qiskit.primitives.StatevectorEstimator` to show their
configuration::

from qiskit.primitives import StatevectorSampler, StatevectorEstimator

sampler = StatevectorSampler(default_shots=2048, seed=42)
print(repr(sampler))
# Output: <StatevectorSampler with default_shots=2048, seed=42>

estimator = StatevectorEstimator(default_precision=0.01, seed=123)
print(repr(estimator))
# Output: <StatevectorEstimator with default_precision=0.01, seed=123>

features_transpiler:
- |
Added ``__repr__`` method to :class:`~qiskit.transpiler.PassManager` to
show the number of pass sets, total passes, max iterations, and property
information::

from qiskit.transpiler import PassManager
from qiskit.transpiler.passes import Optimize1qGates

pm = PassManager(Optimize1qGates(), max_iteration=5)
print(repr(pm))
# Output: <PassManager with 1 sets, 1 passes, 5 max iterations, and 0 properties>

These representations use the ``<ClassName ...>`` format to provide
useful debugging information without attempting to be eval-able, which
is more practical for complex objects.

This addresses issue `#8594 <https://github.com/Qiskit/qiskit/issues/8594>`__.

82 changes: 82 additions & 0 deletions test/python/circuit/test_gate_repr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2024.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at https://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

"""Test __repr__ methods for Gate and ControlledGate classes."""

from qiskit.circuit import Parameter
from qiskit.circuit.library import HGate, CXGate, RXGate, CCXGate
from test import QiskitTestCase


class TestGateRepr(QiskitTestCase):
"""Tests for Gate.__repr__ method."""

def test_gate_repr_basic(self):
"""Test basic Gate repr without label."""
gate = HGate()
result = repr(gate)
# Use actual class name since some gates are singletons
expected = f"<{gate.__class__.__name__} 'h' with 1 qubits, 0 clbits and params=[]>"
self.assertEqual(result, expected)

def test_gate_repr_with_label(self):
"""Test Gate repr with label."""
gate = HGate(label="my_hadamard")
result = repr(gate)
expected = "<HGate 'h' labeled 'my_hadamard' with 1 qubits, 0 clbits and params=[]>"
self.assertEqual(result, expected)

def test_gate_repr_with_params(self):
"""Test Gate repr with parameters."""
gate = RXGate(1.57)
result = repr(gate)
# Build expected string using the actual params from the gate
expected = f"<RXGate 'rx' with 1 qubits, 0 clbits and params={gate.params}>"
self.assertEqual(result, expected)

def test_gate_repr_with_parameter_expression(self):
"""Test Gate repr with Parameter expression."""
theta = Parameter("theta")
gate = RXGate(theta)
result = repr(gate)
# Build expected string using the actual params from the gate
expected = f"<RXGate 'rx' with 1 qubits, 0 clbits and params={gate.params}>"
self.assertEqual(result, expected)


class TestControlledGateRepr(QiskitTestCase):
"""Tests for ControlledGate.__repr__ method."""

def test_controlled_gate_repr_basic(self):
"""Test basic ControlledGate repr without label."""
gate = CXGate()
result = repr(gate)
# Use actual class name since some gates are singletons
expected = f"<{gate.__class__.__name__} 'cx' with 1 control qubits, control state = 1 and params=[]>"
self.assertEqual(result, expected)

def test_controlled_gate_repr_with_label(self):
"""Test ControlledGate repr with label."""
gate = CXGate(label="my_cnot")
result = repr(gate)
expected = (
"<CXGate 'cx' labeled 'my_cnot' with 1 control qubits, control state = 1 and params=[]>"
)
self.assertEqual(result, expected)

def test_controlled_gate_repr_multiple_controls(self):
"""Test ControlledGate repr with multiple control qubits."""
gate = CCXGate()
result = repr(gate)
# Use actual class name since some gates are singletons
expected = f"<{gate.__class__.__name__} 'ccx' with 2 control qubits, control state = 3 and params=[]>"
self.assertEqual(result, expected)
62 changes: 62 additions & 0 deletions test/python/circuit/test_quantum_circuit_repr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2024.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at https://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

"""Test __repr__ method for QuantumCircuit class."""

from qiskit.circuit import QuantumCircuit
from test import QiskitTestCase


class TestQuantumCircuitRepr(QiskitTestCase):
"""Tests for QuantumCircuit.__repr__ method."""

def test_quantum_circuit_repr_basic(self):
"""Test basic QuantumCircuit repr."""
qc = QuantumCircuit(2, 2)
result = repr(qc)
expected = f"<QuantumCircuit '{qc.name}' with 2 qubits, 2 clbits, 0 instructions, and global_phase={qc.global_phase}>"
self.assertEqual(result, expected)

def test_quantum_circuit_repr_with_name(self):
"""Test QuantumCircuit repr with custom name."""
qc = QuantumCircuit(2, 2, name="Bell")
result = repr(qc)
expected = f"<QuantumCircuit 'Bell' with 2 qubits, 2 clbits, 0 instructions, and global_phase={qc.global_phase}>"
self.assertEqual(result, expected)

def test_quantum_circuit_repr_with_instructions(self):
"""Test QuantumCircuit repr with instructions."""
qc = QuantumCircuit(2, 2, name="Bell")
qc.h(0)
qc.cx(0, 1)
qc.measure([0, 1], [0, 1])
result = repr(qc)
# measure([0, 1], [0, 1]) creates 2 measure instructions
expected = f"<QuantumCircuit 'Bell' with 2 qubits, 2 clbits, {len(qc.data)} instructions, and global_phase={qc.global_phase}>"
self.assertEqual(result, expected)

def test_quantum_circuit_repr_with_global_phase(self):
"""Test QuantumCircuit repr with non-zero global phase."""
qc = QuantumCircuit(1, name="PhaseCircuit", global_phase=1.57)
result = repr(qc)
expected = f"<QuantumCircuit 'PhaseCircuit' with 1 qubits, 0 clbits, 0 instructions, and global_phase={qc.global_phase}>"
self.assertEqual(result, expected)

def test_quantum_circuit_repr_no_clbits(self):
"""Test QuantumCircuit repr without classical bits."""
qc = QuantumCircuit(3, name="NoClbits")
qc.h(0)
qc.cx(0, 1)
qc.cx(1, 2)
result = repr(qc)
expected = f"<QuantumCircuit 'NoClbits' with 3 qubits, 0 clbits, 3 instructions, and global_phase={qc.global_phase}>"
self.assertEqual(result, expected)
Loading