Skip to content

Commit

Permalink
Deprecate accidentally public internal helper methods on QuantumCircu…
Browse files Browse the repository at this point in the history
…it (Qiskit#12785)

* Deprecate accidentally public qc helper methods

* Fix lint

* Complete reno and add removal timeline

* Update qiskit/circuit/quantumcircuit.py

Co-authored-by: Julien Gacon <[email protected]>

* Update remaining docstrings and removal timeline.

* Trailing whitespace

---------

Co-authored-by: Julien Gacon <[email protected]>
  • Loading branch information
ElePT and Cryoris committed Jul 24, 2024
1 parent a46208c commit 3e987cd
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -227,8 +227,8 @@ def mcrx(
"""
from .rx import RXGate

control_qubits = self.qbit_argument_conversion(q_controls)
target_qubit = self.qbit_argument_conversion(q_target)
control_qubits = self._qbit_argument_conversion(q_controls)
target_qubit = self._qbit_argument_conversion(q_target)
if len(target_qubit) != 1:
raise QiskitError("The mcrz gate needs a single qubit as target.")
all_qubits = control_qubits + target_qubit
Expand Down Expand Up @@ -292,11 +292,11 @@ def mcry(
"""
from .ry import RYGate

control_qubits = self.qbit_argument_conversion(q_controls)
target_qubit = self.qbit_argument_conversion(q_target)
control_qubits = self._qbit_argument_conversion(q_controls)
target_qubit = self._qbit_argument_conversion(q_target)
if len(target_qubit) != 1:
raise QiskitError("The mcrz gate needs a single qubit as target.")
ancillary_qubits = [] if q_ancillae is None else self.qbit_argument_conversion(q_ancillae)
ancillary_qubits = [] if q_ancillae is None else self._qbit_argument_conversion(q_ancillae)
all_qubits = control_qubits + target_qubit + ancillary_qubits
target_qubit = target_qubit[0]
self._check_dups(all_qubits)
Expand Down Expand Up @@ -365,8 +365,8 @@ def mcrz(
"""
from .rz import CRZGate, RZGate

control_qubits = self.qbit_argument_conversion(q_controls)
target_qubit = self.qbit_argument_conversion(q_target)
control_qubits = self._qbit_argument_conversion(q_controls)
target_qubit = self._qbit_argument_conversion(q_target)
if len(target_qubit) != 1:
raise QiskitError("The mcrz gate needs a single qubit as target.")
all_qubits = control_qubits + target_qubit
Expand Down
107 changes: 93 additions & 14 deletions qiskit/circuit/quantumcircuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
from qiskit.circuit.gate import Gate
from qiskit.circuit.parameter import Parameter
from qiskit.circuit.exceptions import CircuitError
from qiskit.utils import deprecate_func
from . import _classical_resource_map
from ._utils import sort_parameters
from .controlflow import ControlFlowOp, _builder_utils
Expand Down Expand Up @@ -1077,7 +1078,7 @@ def __init__(
self.name: str
"""A human-readable name for the circuit."""
if name is None:
self._base_name = self.cls_prefix()
self._base_name = self._cls_prefix()
self._name_update()
elif not isinstance(name, str):
raise CircuitError(
Expand Down Expand Up @@ -1400,24 +1401,47 @@ def _increment_instances(cls):
cls.instances += 1

@classmethod
@deprecate_func(
since=1.2,
removal_timeline="in the 2.0 release",
additional_msg="This method is only used as an internal helper "
"and will be removed with no replacement.",
)
def cls_instances(cls) -> int:
"""Return the current number of instances of this class,
useful for auto naming."""
return cls.instances

@classmethod
def _cls_instances(cls) -> int:
"""Return the current number of instances of this class,
useful for auto naming."""
return cls.instances

@classmethod
@deprecate_func(
since=1.2,
removal_timeline="in the 2.0 release",
additional_msg="This method is only used as an internal helper "
"and will be removed with no replacement.",
)
def cls_prefix(cls) -> str:
"""Return the prefix to use for auto naming."""
return cls.prefix

@classmethod
def _cls_prefix(cls) -> str:
"""Return the prefix to use for auto naming."""
return cls.prefix

def _name_update(self) -> None:
"""update name of instance using instance number"""
if not is_main_process():
pid_name = f"-{mp.current_process().pid}"
else:
pid_name = ""

self.name = f"{self._base_name}-{self.cls_instances()}{pid_name}"
self.name = f"{self._base_name}-{self._cls_instances()}{pid_name}"

def has_register(self, register: Register) -> bool:
"""
Expand Down Expand Up @@ -1926,7 +1950,7 @@ def replace_var(var: expr.Var, cache: Mapping[expr.Var, expr.Var]) -> expr.Var:
mapped_qubits = dest.qubits
edge_map.update(zip(other.qubits, dest.qubits))
else:
mapped_qubits = dest.qbit_argument_conversion(qubits)
mapped_qubits = dest._qbit_argument_conversion(qubits)
if len(mapped_qubits) != other.num_qubits:
raise CircuitError(
f"Number of items in qubits parameter ({len(mapped_qubits)}) does not"
Expand All @@ -1942,7 +1966,7 @@ def replace_var(var: expr.Var, cache: Mapping[expr.Var, expr.Var]) -> expr.Var:
mapped_clbits = dest.clbits
edge_map.update(zip(other.clbits, dest.clbits))
else:
mapped_clbits = dest.cbit_argument_conversion(clbits)
mapped_clbits = dest._cbit_argument_conversion(clbits)
if len(mapped_clbits) != other.num_clbits:
raise CircuitError(
f"Number of items in clbits parameter ({len(mapped_clbits)}) does not"
Expand All @@ -1952,7 +1976,7 @@ def replace_var(var: expr.Var, cache: Mapping[expr.Var, expr.Var]) -> expr.Var:
raise CircuitError(
f"Duplicate clbits referenced in 'clbits' parameter: '{mapped_clbits}'"
)
edge_map.update(zip(other.clbits, dest.cbit_argument_conversion(clbits)))
edge_map.update(zip(other.clbits, dest._cbit_argument_conversion(clbits)))

for gate, cals in other.calibrations.items():
dest._calibrations[gate].update(cals)
Expand Down Expand Up @@ -2267,38 +2291,91 @@ def __getitem__(self, item):
return self._data[item]

@staticmethod
@deprecate_func(
since=1.2,
removal_timeline="in the 2.0 release",
additional_msg="This method is only used as an internal helper "
"and will be removed with no replacement.",
)
def cast(value: S, type_: Callable[..., T]) -> Union[S, T]:
"""Best effort to cast value to type. Otherwise, returns the value."""
try:
return type_(value)
except (ValueError, TypeError):
return value

@staticmethod
def _cast(value: S, type_: Callable[..., T]) -> Union[S, T]:
"""Best effort to cast value to type. Otherwise, returns the value."""
try:
return type_(value)
except (ValueError, TypeError):
return value

@deprecate_func(
since=1.2,
removal_timeline="in the 2.0 release",
additional_msg="This method is only used as an internal helper "
"and will be removed with no replacement.",
)
def qbit_argument_conversion(self, qubit_representation: QubitSpecifier) -> list[Qubit]:
"""
Converts several qubit representations (such as indexes, range, etc.)
into a list of qubits.
Args:
qubit_representation (Object): representation to expand
qubit_representation: Representation to expand.
Returns:
List(Qubit): the resolved instances of the qubits.
The resolved instances of the qubits.
"""

return self._qbit_argument_conversion(qubit_representation)

def _qbit_argument_conversion(self, qubit_representation: QubitSpecifier) -> list[Qubit]:
"""
Converts several qubit representations (such as indexes, range, etc.)
into a list of qubits.
Args:
qubit_representation: Representation to expand.
Returns:
The resolved instances of the qubits.
"""
return _bit_argument_conversion(
qubit_representation, self.qubits, self._qubit_indices, Qubit
)

@deprecate_func(
since=1.2,
removal_timeline="in the 2.0 release",
additional_msg="This method is only used as an internal helper "
"and will be removed with no replacement.",
)
def cbit_argument_conversion(self, clbit_representation: ClbitSpecifier) -> list[Clbit]:
"""
Converts several classical bit representations (such as indexes, range, etc.)
into a list of classical bits.
Args:
clbit_representation (Object): representation to expand
clbit_representation : Representation to expand.
Returns:
List(tuple): Where each tuple is a classical bit.
A list of tuples where each tuple is a classical bit.
"""
return self._cbit_argument_conversion(clbit_representation)

def _cbit_argument_conversion(self, clbit_representation: ClbitSpecifier) -> list[Clbit]:
"""
Converts several classical bit representations (such as indexes, range, etc.)
into a list of classical bits.
Args:
clbit_representation: Representation to expand.
Returns:
A list of tuples where each tuple is a classical bit.
"""
return _bit_argument_conversion(
clbit_representation, self.clbits, self._clbit_indices, Clbit
Expand All @@ -2317,7 +2394,7 @@ def _append_standard_gate(
if params is None:
params = []

expanded_qargs = [self.qbit_argument_conversion(qarg) for qarg in qargs or []]
expanded_qargs = [self._qbit_argument_conversion(qarg) for qarg in qargs or []]
for param in params:
Gate.validate_parameter(op, param)

Expand Down Expand Up @@ -2416,8 +2493,8 @@ def append(
" which are not in this circuit"
)

expanded_qargs = [self.qbit_argument_conversion(qarg) for qarg in qargs or []]
expanded_cargs = [self.cbit_argument_conversion(carg) for carg in cargs or []]
expanded_qargs = [self._qbit_argument_conversion(qarg) for qarg in qargs or []]
expanded_cargs = [self._cbit_argument_conversion(carg) for carg in cargs or []]

instructions = InstructionSet(resource_requester=circuit_scope.resolve_classical_resource)
# For Operations that are non-Instructions, we use the Instruction's default method
Expand Down Expand Up @@ -4416,7 +4493,9 @@ def barrier(self, *qargs: QubitSpecifier, label=None) -> InstructionSet:

if qargs:
# This uses a `dict` not a `set` to guarantee a deterministic order to the arguments.
qubits = tuple({q: None for qarg in qargs for q in self.qbit_argument_conversion(qarg)})
qubits = tuple(
{q: None for qarg in qargs for q in self._qbit_argument_conversion(qarg)}
)
return self.append(
CircuitInstruction(Barrier(len(qubits), label=label), qubits, ()), copy=False
)
Expand Down Expand Up @@ -5448,7 +5527,7 @@ def mcx(

# check ancilla input
if ancilla_qubits:
_ = self.qbit_argument_conversion(ancilla_qubits)
_ = self._qbit_argument_conversion(ancilla_qubits)

try:
gate = available_implementations[mode]
Expand Down
4 changes: 2 additions & 2 deletions qiskit/circuit/quantumcircuitdata.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ def _resolve_legacy_value(self, operation, qargs, cargs) -> CircuitInstruction:
if not isinstance(operation, Operation):
raise CircuitError("object is not an Operation.")

expanded_qargs = [self._circuit.qbit_argument_conversion(qarg) for qarg in qargs or []]
expanded_cargs = [self._circuit.cbit_argument_conversion(carg) for carg in cargs or []]
expanded_qargs = [self._circuit._qbit_argument_conversion(qarg) for qarg in qargs or []]
expanded_cargs = [self._circuit._cbit_argument_conversion(carg) for carg in cargs or []]

if isinstance(operation, Instruction):
broadcast_args = list(operation.broadcast_arguments(expanded_qargs, expanded_cargs))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

from qiskit.circuit.controlflow import CONTROL_FLOW_OP_NAMES
from qiskit.circuit.library.standard_gates import get_standard_gate_name_mapping
from qiskit.providers.backend import Backend
from qiskit.providers.backend_compat import BackendV2Converter
from qiskit.transpiler.coupling import CouplingMap
from qiskit.transpiler.exceptions import TranspilerError
Expand All @@ -33,7 +34,7 @@


def generate_preset_pass_manager(
optimization_level,
optimization_level=2,
backend=None,
target=None,
basis_gates=None,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
deprecations_circuits:
- |
The following circuit methods were not intended for public use, but were accidentally left documented in the
public API during the 1.0 release. They are now deprecated from Qiskit 1.2 and will be removed in Qiskit 2.0:
* ``QuantumCircuit.cast``
* ``QuantumCircuit.cls_instances``
* ``QuantumCircuit.cls_prefix``
* ``QuantumCircuit.cbit_argument_conversion``
* ``QuantumCircuit.qbit_argument_conversion``

0 comments on commit 3e987cd

Please sign in to comment.