mirror of https://github.com/Qiskit/qiskit.git
Add `copy` keyword argument to `QuantumCircuit.append` (#12098)
We always copy compile-time parametric instructions during `QuantumCircuit.append` to avoid accidental mutation of other references in a call to `assign_parameters(inplace=True)`. However, mostly our rotation gates are added through things like `QuantumCircuit.rz`, where the only reference to the `RZGate` is internal to the `QuantumCircuit`, so creating and immediately copying it is a waste of time. This commit adds a `copy` argument to `QuantumCircuit.append`, so we can set it to `False` in places where we know we own the instruction being added.
This commit is contained in:
parent
9ed963f1aa
commit
44cbb7cec9
|
@ -127,10 +127,12 @@ class BlueprintCircuit(QuantumCircuit, ABC):
|
|||
self._build()
|
||||
return super()._append(instruction, _qargs, _cargs)
|
||||
|
||||
def compose(self, other, qubits=None, clbits=None, front=False, inplace=False, wrap=False):
|
||||
def compose(
|
||||
self, other, qubits=None, clbits=None, front=False, inplace=False, wrap=False, *, copy=True
|
||||
):
|
||||
if not self._is_built:
|
||||
self._build()
|
||||
return super().compose(other, qubits, clbits, front, inplace, wrap)
|
||||
return super().compose(other, qubits, clbits, front, inplace, wrap, copy=copy)
|
||||
|
||||
def inverse(self, annotated: bool = False):
|
||||
if not self._is_built:
|
||||
|
|
|
@ -87,7 +87,7 @@ class PhaseOracle(QuantumCircuit):
|
|||
|
||||
super().__init__(oracle.num_qubits, name="Phase Oracle")
|
||||
|
||||
self.compose(oracle, inplace=True)
|
||||
self.compose(oracle, inplace=True, copy=False)
|
||||
|
||||
def evaluate_bitstring(self, bitstring: str) -> bool:
|
||||
"""Evaluate the oracle on a bitstring.
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
"""Quantum circuit object."""
|
||||
|
||||
from __future__ import annotations
|
||||
import copy
|
||||
import copy as _copy
|
||||
import itertools
|
||||
import multiprocessing as mp
|
||||
import typing
|
||||
|
@ -425,10 +425,10 @@ class QuantumCircuit:
|
|||
return
|
||||
if isinstance(data_input[0], CircuitInstruction):
|
||||
for instruction in data_input:
|
||||
self.append(instruction)
|
||||
self.append(instruction, copy=False)
|
||||
else:
|
||||
for instruction, qargs, cargs in data_input:
|
||||
self.append(instruction, qargs, cargs)
|
||||
self.append(instruction, qargs, cargs, copy=False)
|
||||
|
||||
@property
|
||||
def op_start_times(self) -> list[int]:
|
||||
|
@ -535,7 +535,7 @@ class QuantumCircuit:
|
|||
cls = self.__class__
|
||||
result = cls.__new__(cls)
|
||||
for k in self.__dict__.keys() - {"_data", "_builder_api"}:
|
||||
setattr(result, k, copy.deepcopy(self.__dict__[k], memo))
|
||||
setattr(result, k, _copy.deepcopy(self.__dict__[k], memo))
|
||||
|
||||
result._builder_api = _OuterCircuitScopeInterface(result)
|
||||
|
||||
|
@ -543,10 +543,10 @@ class QuantumCircuit:
|
|||
# like we would when pickling.
|
||||
result._data = self._data.copy()
|
||||
result._data.replace_bits(
|
||||
qubits=copy.deepcopy(self._data.qubits, memo),
|
||||
clbits=copy.deepcopy(self._data.clbits, memo),
|
||||
qubits=_copy.deepcopy(self._data.qubits, memo),
|
||||
clbits=_copy.deepcopy(self._data.clbits, memo),
|
||||
)
|
||||
result._data.map_ops(lambda op: copy.deepcopy(op, memo))
|
||||
result._data.map_ops(lambda op: _copy.deepcopy(op, memo))
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
|
@ -869,6 +869,8 @@ class QuantumCircuit:
|
|||
front: bool = False,
|
||||
inplace: bool = False,
|
||||
wrap: bool = False,
|
||||
*,
|
||||
copy: bool = True,
|
||||
) -> Optional["QuantumCircuit"]:
|
||||
"""Compose circuit with ``other`` circuit or instruction, optionally permuting wires.
|
||||
|
||||
|
@ -885,6 +887,12 @@ class QuantumCircuit:
|
|||
inplace (bool): If True, modify the object. Otherwise return composed circuit.
|
||||
wrap (bool): If True, wraps the other circuit into a gate (or instruction, depending on
|
||||
whether it contains only unitary instructions) before composing it onto self.
|
||||
copy (bool): If ``True`` (the default), then the input is treated as shared, and any
|
||||
contained instructions will be copied, if they might need to be mutated in the
|
||||
future. You can set this to ``False`` if the input should be considered owned by
|
||||
the base circuit, in order to avoid unnecessary copies; in this case, it is not
|
||||
valid to use ``other`` afterwards, and some instructions may have been mutated in
|
||||
place.
|
||||
|
||||
Returns:
|
||||
QuantumCircuit: the composed circuit (returns None if inplace==True).
|
||||
|
@ -969,11 +977,11 @@ class QuantumCircuit:
|
|||
# Need to keep a reference to the data for use after we've emptied it.
|
||||
old_data = dest._data.copy()
|
||||
dest.clear()
|
||||
dest.append(other, qubits, clbits)
|
||||
dest.append(other, qubits, clbits, copy=copy)
|
||||
for instruction in old_data:
|
||||
dest._append(instruction)
|
||||
else:
|
||||
dest.append(other, qargs=qubits, cargs=clbits)
|
||||
dest.append(other, qargs=qubits, cargs=clbits, copy=copy)
|
||||
return None if inplace else dest
|
||||
|
||||
if other.num_qubits > dest.num_qubits or other.num_clbits > dest.num_clbits:
|
||||
|
@ -1035,10 +1043,11 @@ class QuantumCircuit:
|
|||
)
|
||||
|
||||
def map_vars(op):
|
||||
n_op = op.copy()
|
||||
n_op = op.copy() if copy else op
|
||||
if (condition := getattr(n_op, "condition", None)) is not None:
|
||||
n_op.condition = variable_mapper.map_condition(condition)
|
||||
if isinstance(n_op, SwitchCaseOp):
|
||||
n_op = n_op.copy() if n_op is op else n_op
|
||||
n_op.target = variable_mapper.map_target(n_op.target)
|
||||
return n_op
|
||||
|
||||
|
@ -1312,6 +1321,8 @@ class QuantumCircuit:
|
|||
instruction: Operation | CircuitInstruction,
|
||||
qargs: Sequence[QubitSpecifier] | None = None,
|
||||
cargs: Sequence[ClbitSpecifier] | None = None,
|
||||
*,
|
||||
copy: bool = True,
|
||||
) -> InstructionSet:
|
||||
"""Append one or more instructions to the end of the circuit, modifying the circuit in
|
||||
place.
|
||||
|
@ -1329,6 +1340,11 @@ class QuantumCircuit:
|
|||
:class:`.CircuitInstruction` with all its context.
|
||||
qargs: specifiers of the :class:`~.circuit.Qubit`\\ s to attach instruction to.
|
||||
cargs: specifiers of the :class:`.Clbit`\\ s to attach instruction to.
|
||||
copy: if ``True`` (the default), then the incoming ``instruction`` is copied before
|
||||
adding it to the circuit if it contains symbolic parameters, so it can be safely
|
||||
mutated without affecting other circuits the same instruction might be in. If you
|
||||
are sure this instruction will not be in other circuits, you can set this ``False``
|
||||
for a small speedup.
|
||||
|
||||
Returns:
|
||||
qiskit.circuit.InstructionSet: a handle to the :class:`.CircuitInstruction`\\ s that
|
||||
|
@ -1367,11 +1383,11 @@ class QuantumCircuit:
|
|||
if params := getattr(operation, "params", ()):
|
||||
is_parameter = False
|
||||
for param in params:
|
||||
is_parameter = is_parameter or isinstance(param, Parameter)
|
||||
is_parameter = is_parameter or isinstance(param, ParameterExpression)
|
||||
if isinstance(param, expr.Expr):
|
||||
param = _validate_expr(circuit_scope, param)
|
||||
if is_parameter:
|
||||
operation = copy.deepcopy(operation)
|
||||
if copy and is_parameter:
|
||||
operation = _copy.deepcopy(operation)
|
||||
if isinstance(operation, ControlFlowOp):
|
||||
# Verify that any variable bindings are valid. Control-flow ops are already enforced
|
||||
# by the class not to contain 'input' variables.
|
||||
|
@ -2543,7 +2559,7 @@ class QuantumCircuit:
|
|||
raise TypeError(
|
||||
f"invalid name for a circuit: '{name}'. The name must be a string or 'None'."
|
||||
)
|
||||
cpy = copy.copy(self)
|
||||
cpy = _copy.copy(self)
|
||||
# copy registers correctly, in copy.copy they are only copied via reference
|
||||
cpy.qregs = self.qregs.copy()
|
||||
cpy.cregs = self.cregs.copy()
|
||||
|
@ -2566,8 +2582,8 @@ class QuantumCircuit:
|
|||
)
|
||||
cpy._data = CircuitData(self._data.qubits, self._data.clbits)
|
||||
|
||||
cpy._calibrations = copy.deepcopy(self._calibrations)
|
||||
cpy._metadata = copy.deepcopy(self._metadata)
|
||||
cpy._calibrations = _copy.deepcopy(self._calibrations)
|
||||
cpy._metadata = _copy.deepcopy(self._metadata)
|
||||
|
||||
if name:
|
||||
cpy.name = name
|
||||
|
@ -2616,7 +2632,7 @@ class QuantumCircuit:
|
|||
"""
|
||||
from .reset import Reset
|
||||
|
||||
return self.append(Reset(), [qubit], [])
|
||||
return self.append(Reset(), [qubit], [], copy=False)
|
||||
|
||||
def store(self, lvalue: typing.Any, rvalue: typing.Any, /) -> InstructionSet:
|
||||
"""Store the result of the given real-time classical expression ``rvalue`` in the memory
|
||||
|
@ -2641,7 +2657,7 @@ class QuantumCircuit:
|
|||
:meth:`add_var`
|
||||
Create a new variable in the circuit that can be written to with this method.
|
||||
"""
|
||||
return self.append(Store(expr.lift(lvalue), expr.lift(rvalue)), (), ())
|
||||
return self.append(Store(expr.lift(lvalue), expr.lift(rvalue)), (), (), copy=False)
|
||||
|
||||
def measure(self, qubit: QubitSpecifier, cbit: ClbitSpecifier) -> InstructionSet:
|
||||
r"""Measure a quantum bit (``qubit``) in the Z basis into a classical bit (``cbit``).
|
||||
|
@ -2718,7 +2734,7 @@ class QuantumCircuit:
|
|||
"""
|
||||
from .measure import Measure
|
||||
|
||||
return self.append(Measure(), [qubit], [cbit])
|
||||
return self.append(Measure(), [qubit], [cbit], copy=False)
|
||||
|
||||
def measure_active(self, inplace: bool = True) -> Optional["QuantumCircuit"]:
|
||||
"""Adds measurement to all non-idle qubits. Creates a new ClassicalRegister with
|
||||
|
@ -3294,7 +3310,9 @@ class QuantumCircuit:
|
|||
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)})
|
||||
return self.append(CircuitInstruction(Barrier(len(qubits), label=label), qubits, ()))
|
||||
return self.append(
|
||||
CircuitInstruction(Barrier(len(qubits), label=label), qubits, ()), copy=False
|
||||
)
|
||||
else:
|
||||
qubits = self.qubits.copy()
|
||||
return self._current_scope().append(
|
||||
|
@ -3325,7 +3343,7 @@ class QuantumCircuit:
|
|||
"""
|
||||
if qarg is None:
|
||||
qarg = self.qubits
|
||||
return self.append(Delay(duration, unit=unit), [qarg], [])
|
||||
return self.append(Delay(duration, unit=unit), [qarg], [], copy=False)
|
||||
|
||||
def h(self, qubit: QubitSpecifier) -> InstructionSet:
|
||||
"""Apply :class:`~qiskit.circuit.library.HGate`.
|
||||
|
@ -3340,7 +3358,7 @@ class QuantumCircuit:
|
|||
"""
|
||||
from .library.standard_gates.h import HGate
|
||||
|
||||
return self.append(HGate(), [qubit], [])
|
||||
return self.append(HGate(), [qubit], [], copy=False)
|
||||
|
||||
def ch(
|
||||
self,
|
||||
|
@ -3367,7 +3385,10 @@ class QuantumCircuit:
|
|||
from .library.standard_gates.h import CHGate
|
||||
|
||||
return self.append(
|
||||
CHGate(label=label, ctrl_state=ctrl_state), [control_qubit, target_qubit], []
|
||||
CHGate(label=label, ctrl_state=ctrl_state),
|
||||
[control_qubit, target_qubit],
|
||||
[],
|
||||
copy=False,
|
||||
)
|
||||
|
||||
def id(self, qubit: QubitSpecifier) -> InstructionSet: # pylint: disable=invalid-name
|
||||
|
@ -3383,7 +3404,7 @@ class QuantumCircuit:
|
|||
"""
|
||||
from .library.standard_gates.i import IGate
|
||||
|
||||
return self.append(IGate(), [qubit], [])
|
||||
return self.append(IGate(), [qubit], [], copy=False)
|
||||
|
||||
def ms(self, theta: ParameterValueType, qubits: Sequence[QubitSpecifier]) -> InstructionSet:
|
||||
"""Apply :class:`~qiskit.circuit.library.MSGate`.
|
||||
|
@ -3400,7 +3421,7 @@ class QuantumCircuit:
|
|||
# pylint: disable=cyclic-import
|
||||
from .library.generalized_gates.gms import MSGate
|
||||
|
||||
return self.append(MSGate(len(qubits), theta), qubits)
|
||||
return self.append(MSGate(len(qubits), theta), qubits, copy=False)
|
||||
|
||||
def p(self, theta: ParameterValueType, qubit: QubitSpecifier) -> InstructionSet:
|
||||
"""Apply :class:`~qiskit.circuit.library.PhaseGate`.
|
||||
|
@ -3416,7 +3437,7 @@ class QuantumCircuit:
|
|||
"""
|
||||
from .library.standard_gates.p import PhaseGate
|
||||
|
||||
return self.append(PhaseGate(theta), [qubit], [])
|
||||
return self.append(PhaseGate(theta), [qubit], [], copy=False)
|
||||
|
||||
def cp(
|
||||
self,
|
||||
|
@ -3445,7 +3466,10 @@ class QuantumCircuit:
|
|||
from .library.standard_gates.p import CPhaseGate
|
||||
|
||||
return self.append(
|
||||
CPhaseGate(theta, label=label, ctrl_state=ctrl_state), [control_qubit, target_qubit], []
|
||||
CPhaseGate(theta, label=label, ctrl_state=ctrl_state),
|
||||
[control_qubit, target_qubit],
|
||||
[],
|
||||
copy=False,
|
||||
)
|
||||
|
||||
def mcp(
|
||||
|
@ -3477,6 +3501,7 @@ class QuantumCircuit:
|
|||
MCPhaseGate(lam, num_ctrl_qubits, ctrl_state=ctrl_state),
|
||||
control_qubits[:] + [target_qubit],
|
||||
[],
|
||||
copy=False,
|
||||
)
|
||||
|
||||
def r(
|
||||
|
@ -3496,7 +3521,7 @@ class QuantumCircuit:
|
|||
"""
|
||||
from .library.standard_gates.r import RGate
|
||||
|
||||
return self.append(RGate(theta, phi), [qubit], [])
|
||||
return self.append(RGate(theta, phi), [qubit], [], copy=False)
|
||||
|
||||
def rv(
|
||||
self,
|
||||
|
@ -3523,7 +3548,7 @@ class QuantumCircuit:
|
|||
"""
|
||||
from .library.generalized_gates.rv import RVGate
|
||||
|
||||
return self.append(RVGate(vx, vy, vz), [qubit], [])
|
||||
return self.append(RVGate(vx, vy, vz), [qubit], [], copy=False)
|
||||
|
||||
def rccx(
|
||||
self,
|
||||
|
@ -3545,7 +3570,9 @@ class QuantumCircuit:
|
|||
"""
|
||||
from .library.standard_gates.x import RCCXGate
|
||||
|
||||
return self.append(RCCXGate(), [control_qubit1, control_qubit2, target_qubit], [])
|
||||
return self.append(
|
||||
RCCXGate(), [control_qubit1, control_qubit2, target_qubit], [], copy=False
|
||||
)
|
||||
|
||||
def rcccx(
|
||||
self,
|
||||
|
@ -3570,7 +3597,10 @@ class QuantumCircuit:
|
|||
from .library.standard_gates.x import RC3XGate
|
||||
|
||||
return self.append(
|
||||
RC3XGate(), [control_qubit1, control_qubit2, control_qubit3, target_qubit], []
|
||||
RC3XGate(),
|
||||
[control_qubit1, control_qubit2, control_qubit3, target_qubit],
|
||||
[],
|
||||
copy=False,
|
||||
)
|
||||
|
||||
def rx(
|
||||
|
@ -3590,7 +3620,7 @@ class QuantumCircuit:
|
|||
"""
|
||||
from .library.standard_gates.rx import RXGate
|
||||
|
||||
return self.append(RXGate(theta, label=label), [qubit], [])
|
||||
return self.append(RXGate(theta, label=label), [qubit], [], copy=False)
|
||||
|
||||
def crx(
|
||||
self,
|
||||
|
@ -3619,7 +3649,10 @@ class QuantumCircuit:
|
|||
from .library.standard_gates.rx import CRXGate
|
||||
|
||||
return self.append(
|
||||
CRXGate(theta, label=label, ctrl_state=ctrl_state), [control_qubit, target_qubit], []
|
||||
CRXGate(theta, label=label, ctrl_state=ctrl_state),
|
||||
[control_qubit, target_qubit],
|
||||
[],
|
||||
copy=False,
|
||||
)
|
||||
|
||||
def rxx(
|
||||
|
@ -3639,7 +3672,7 @@ class QuantumCircuit:
|
|||
"""
|
||||
from .library.standard_gates.rxx import RXXGate
|
||||
|
||||
return self.append(RXXGate(theta), [qubit1, qubit2], [])
|
||||
return self.append(RXXGate(theta), [qubit1, qubit2], [], copy=False)
|
||||
|
||||
def ry(
|
||||
self, theta: ParameterValueType, qubit: QubitSpecifier, label: str | None = None
|
||||
|
@ -3658,7 +3691,7 @@ class QuantumCircuit:
|
|||
"""
|
||||
from .library.standard_gates.ry import RYGate
|
||||
|
||||
return self.append(RYGate(theta, label=label), [qubit], [])
|
||||
return self.append(RYGate(theta, label=label), [qubit], [], copy=False)
|
||||
|
||||
def cry(
|
||||
self,
|
||||
|
@ -3687,7 +3720,10 @@ class QuantumCircuit:
|
|||
from .library.standard_gates.ry import CRYGate
|
||||
|
||||
return self.append(
|
||||
CRYGate(theta, label=label, ctrl_state=ctrl_state), [control_qubit, target_qubit], []
|
||||
CRYGate(theta, label=label, ctrl_state=ctrl_state),
|
||||
[control_qubit, target_qubit],
|
||||
[],
|
||||
copy=False,
|
||||
)
|
||||
|
||||
def ryy(
|
||||
|
@ -3707,7 +3743,7 @@ class QuantumCircuit:
|
|||
"""
|
||||
from .library.standard_gates.ryy import RYYGate
|
||||
|
||||
return self.append(RYYGate(theta), [qubit1, qubit2], [])
|
||||
return self.append(RYYGate(theta), [qubit1, qubit2], [], copy=False)
|
||||
|
||||
def rz(self, phi: ParameterValueType, qubit: QubitSpecifier) -> InstructionSet:
|
||||
"""Apply :class:`~qiskit.circuit.library.RZGate`.
|
||||
|
@ -3723,7 +3759,7 @@ class QuantumCircuit:
|
|||
"""
|
||||
from .library.standard_gates.rz import RZGate
|
||||
|
||||
return self.append(RZGate(phi), [qubit], [])
|
||||
return self.append(RZGate(phi), [qubit], [], copy=False)
|
||||
|
||||
def crz(
|
||||
self,
|
||||
|
@ -3752,7 +3788,10 @@ class QuantumCircuit:
|
|||
from .library.standard_gates.rz import CRZGate
|
||||
|
||||
return self.append(
|
||||
CRZGate(theta, label=label, ctrl_state=ctrl_state), [control_qubit, target_qubit], []
|
||||
CRZGate(theta, label=label, ctrl_state=ctrl_state),
|
||||
[control_qubit, target_qubit],
|
||||
[],
|
||||
copy=False,
|
||||
)
|
||||
|
||||
def rzx(
|
||||
|
@ -3772,7 +3811,7 @@ class QuantumCircuit:
|
|||
"""
|
||||
from .library.standard_gates.rzx import RZXGate
|
||||
|
||||
return self.append(RZXGate(theta), [qubit1, qubit2], [])
|
||||
return self.append(RZXGate(theta), [qubit1, qubit2], [], copy=False)
|
||||
|
||||
def rzz(
|
||||
self, theta: ParameterValueType, qubit1: QubitSpecifier, qubit2: QubitSpecifier
|
||||
|
@ -3791,7 +3830,7 @@ class QuantumCircuit:
|
|||
"""
|
||||
from .library.standard_gates.rzz import RZZGate
|
||||
|
||||
return self.append(RZZGate(theta), [qubit1, qubit2], [])
|
||||
return self.append(RZZGate(theta), [qubit1, qubit2], [], copy=False)
|
||||
|
||||
def ecr(self, qubit1: QubitSpecifier, qubit2: QubitSpecifier) -> InstructionSet:
|
||||
"""Apply :class:`~qiskit.circuit.library.ECRGate`.
|
||||
|
@ -3806,7 +3845,7 @@ class QuantumCircuit:
|
|||
"""
|
||||
from .library.standard_gates.ecr import ECRGate
|
||||
|
||||
return self.append(ECRGate(), [qubit1, qubit2], [])
|
||||
return self.append(ECRGate(), [qubit1, qubit2], [], copy=False)
|
||||
|
||||
def s(self, qubit: QubitSpecifier) -> InstructionSet:
|
||||
"""Apply :class:`~qiskit.circuit.library.SGate`.
|
||||
|
@ -3821,7 +3860,7 @@ class QuantumCircuit:
|
|||
"""
|
||||
from .library.standard_gates.s import SGate
|
||||
|
||||
return self.append(SGate(), [qubit], [])
|
||||
return self.append(SGate(), [qubit], [], copy=False)
|
||||
|
||||
def sdg(self, qubit: QubitSpecifier) -> InstructionSet:
|
||||
"""Apply :class:`~qiskit.circuit.library.SdgGate`.
|
||||
|
@ -3836,7 +3875,7 @@ class QuantumCircuit:
|
|||
"""
|
||||
from .library.standard_gates.s import SdgGate
|
||||
|
||||
return self.append(SdgGate(), [qubit], [])
|
||||
return self.append(SdgGate(), [qubit], [], copy=False)
|
||||
|
||||
def cs(
|
||||
self,
|
||||
|
@ -3866,6 +3905,7 @@ class QuantumCircuit:
|
|||
CSGate(label=label, ctrl_state=ctrl_state),
|
||||
[control_qubit, target_qubit],
|
||||
[],
|
||||
copy=False,
|
||||
)
|
||||
|
||||
def csdg(
|
||||
|
@ -3896,6 +3936,7 @@ class QuantumCircuit:
|
|||
CSdgGate(label=label, ctrl_state=ctrl_state),
|
||||
[control_qubit, target_qubit],
|
||||
[],
|
||||
copy=False,
|
||||
)
|
||||
|
||||
def swap(self, qubit1: QubitSpecifier, qubit2: QubitSpecifier) -> InstructionSet:
|
||||
|
@ -3911,7 +3952,7 @@ class QuantumCircuit:
|
|||
"""
|
||||
from .library.standard_gates.swap import SwapGate
|
||||
|
||||
return self.append(SwapGate(), [qubit1, qubit2], [])
|
||||
return self.append(SwapGate(), [qubit1, qubit2], [], copy=False)
|
||||
|
||||
def iswap(self, qubit1: QubitSpecifier, qubit2: QubitSpecifier) -> InstructionSet:
|
||||
"""Apply :class:`~qiskit.circuit.library.iSwapGate`.
|
||||
|
@ -3926,7 +3967,7 @@ class QuantumCircuit:
|
|||
"""
|
||||
from .library.standard_gates.iswap import iSwapGate
|
||||
|
||||
return self.append(iSwapGate(), [qubit1, qubit2], [])
|
||||
return self.append(iSwapGate(), [qubit1, qubit2], [], copy=False)
|
||||
|
||||
def cswap(
|
||||
self,
|
||||
|
@ -3958,6 +3999,7 @@ class QuantumCircuit:
|
|||
CSwapGate(label=label, ctrl_state=ctrl_state),
|
||||
[control_qubit, target_qubit1, target_qubit2],
|
||||
[],
|
||||
copy=False,
|
||||
)
|
||||
|
||||
def sx(self, qubit: QubitSpecifier) -> InstructionSet:
|
||||
|
@ -3973,7 +4015,7 @@ class QuantumCircuit:
|
|||
"""
|
||||
from .library.standard_gates.sx import SXGate
|
||||
|
||||
return self.append(SXGate(), [qubit], [])
|
||||
return self.append(SXGate(), [qubit], [], copy=False)
|
||||
|
||||
def sxdg(self, qubit: QubitSpecifier) -> InstructionSet:
|
||||
"""Apply :class:`~qiskit.circuit.library.SXdgGate`.
|
||||
|
@ -3988,7 +4030,7 @@ class QuantumCircuit:
|
|||
"""
|
||||
from .library.standard_gates.sx import SXdgGate
|
||||
|
||||
return self.append(SXdgGate(), [qubit], [])
|
||||
return self.append(SXdgGate(), [qubit], [], copy=False)
|
||||
|
||||
def csx(
|
||||
self,
|
||||
|
@ -4018,6 +4060,7 @@ class QuantumCircuit:
|
|||
CSXGate(label=label, ctrl_state=ctrl_state),
|
||||
[control_qubit, target_qubit],
|
||||
[],
|
||||
copy=False,
|
||||
)
|
||||
|
||||
def t(self, qubit: QubitSpecifier) -> InstructionSet:
|
||||
|
@ -4033,7 +4076,7 @@ class QuantumCircuit:
|
|||
"""
|
||||
from .library.standard_gates.t import TGate
|
||||
|
||||
return self.append(TGate(), [qubit], [])
|
||||
return self.append(TGate(), [qubit], [], copy=False)
|
||||
|
||||
def tdg(self, qubit: QubitSpecifier) -> InstructionSet:
|
||||
"""Apply :class:`~qiskit.circuit.library.TdgGate`.
|
||||
|
@ -4048,7 +4091,7 @@ class QuantumCircuit:
|
|||
"""
|
||||
from .library.standard_gates.t import TdgGate
|
||||
|
||||
return self.append(TdgGate(), [qubit], [])
|
||||
return self.append(TdgGate(), [qubit], [], copy=False)
|
||||
|
||||
def u(
|
||||
self,
|
||||
|
@ -4072,7 +4115,7 @@ class QuantumCircuit:
|
|||
"""
|
||||
from .library.standard_gates.u import UGate
|
||||
|
||||
return self.append(UGate(theta, phi, lam), [qubit], [])
|
||||
return self.append(UGate(theta, phi, lam), [qubit], [], copy=False)
|
||||
|
||||
def cu(
|
||||
self,
|
||||
|
@ -4110,6 +4153,7 @@ class QuantumCircuit:
|
|||
CUGate(theta, phi, lam, gamma, label=label, ctrl_state=ctrl_state),
|
||||
[control_qubit, target_qubit],
|
||||
[],
|
||||
copy=False,
|
||||
)
|
||||
|
||||
def x(self, qubit: QubitSpecifier, label: str | None = None) -> InstructionSet:
|
||||
|
@ -4126,7 +4170,7 @@ class QuantumCircuit:
|
|||
"""
|
||||
from .library.standard_gates.x import XGate
|
||||
|
||||
return self.append(XGate(label=label), [qubit], [])
|
||||
return self.append(XGate(label=label), [qubit], [], copy=False)
|
||||
|
||||
def cx(
|
||||
self,
|
||||
|
@ -4154,7 +4198,10 @@ class QuantumCircuit:
|
|||
from .library.standard_gates.x import CXGate
|
||||
|
||||
return self.append(
|
||||
CXGate(label=label, ctrl_state=ctrl_state), [control_qubit, target_qubit], []
|
||||
CXGate(label=label, ctrl_state=ctrl_state),
|
||||
[control_qubit, target_qubit],
|
||||
[],
|
||||
copy=False,
|
||||
)
|
||||
|
||||
def dcx(self, qubit1: QubitSpecifier, qubit2: QubitSpecifier) -> InstructionSet:
|
||||
|
@ -4171,7 +4218,7 @@ class QuantumCircuit:
|
|||
"""
|
||||
from .library.standard_gates.dcx import DCXGate
|
||||
|
||||
return self.append(DCXGate(), [qubit1, qubit2], [])
|
||||
return self.append(DCXGate(), [qubit1, qubit2], [], copy=False)
|
||||
|
||||
def ccx(
|
||||
self,
|
||||
|
@ -4201,6 +4248,7 @@ class QuantumCircuit:
|
|||
CCXGate(ctrl_state=ctrl_state),
|
||||
[control_qubit1, control_qubit2, target_qubit],
|
||||
[],
|
||||
copy=False,
|
||||
)
|
||||
|
||||
def mcx(
|
||||
|
@ -4300,7 +4348,7 @@ class QuantumCircuit:
|
|||
"""
|
||||
from .library.standard_gates.y import YGate
|
||||
|
||||
return self.append(YGate(), [qubit], [])
|
||||
return self.append(YGate(), [qubit], [], copy=False)
|
||||
|
||||
def cy(
|
||||
self,
|
||||
|
@ -4327,7 +4375,10 @@ class QuantumCircuit:
|
|||
from .library.standard_gates.y import CYGate
|
||||
|
||||
return self.append(
|
||||
CYGate(label=label, ctrl_state=ctrl_state), [control_qubit, target_qubit], []
|
||||
CYGate(label=label, ctrl_state=ctrl_state),
|
||||
[control_qubit, target_qubit],
|
||||
[],
|
||||
copy=False,
|
||||
)
|
||||
|
||||
def z(self, qubit: QubitSpecifier) -> InstructionSet:
|
||||
|
@ -4343,7 +4394,7 @@ class QuantumCircuit:
|
|||
"""
|
||||
from .library.standard_gates.z import ZGate
|
||||
|
||||
return self.append(ZGate(), [qubit], [])
|
||||
return self.append(ZGate(), [qubit], [], copy=False)
|
||||
|
||||
def cz(
|
||||
self,
|
||||
|
@ -4370,7 +4421,10 @@ class QuantumCircuit:
|
|||
from .library.standard_gates.z import CZGate
|
||||
|
||||
return self.append(
|
||||
CZGate(label=label, ctrl_state=ctrl_state), [control_qubit, target_qubit], []
|
||||
CZGate(label=label, ctrl_state=ctrl_state),
|
||||
[control_qubit, target_qubit],
|
||||
[],
|
||||
copy=False,
|
||||
)
|
||||
|
||||
def ccz(
|
||||
|
@ -4403,6 +4457,7 @@ class QuantumCircuit:
|
|||
CCZGate(label=label, ctrl_state=ctrl_state),
|
||||
[control_qubit1, control_qubit2, target_qubit],
|
||||
[],
|
||||
copy=False,
|
||||
)
|
||||
|
||||
def pauli(
|
||||
|
@ -4421,7 +4476,7 @@ class QuantumCircuit:
|
|||
"""
|
||||
from qiskit.circuit.library.generalized_gates.pauli import PauliGate
|
||||
|
||||
return self.append(PauliGate(pauli_string), qubits, [])
|
||||
return self.append(PauliGate(pauli_string), qubits, [], copy=False)
|
||||
|
||||
def prepare_state(
|
||||
self,
|
||||
|
@ -4532,7 +4587,9 @@ class QuantumCircuit:
|
|||
num_qubits = len(qubits) if isinstance(state, int) else None
|
||||
|
||||
return self.append(
|
||||
StatePreparation(state, num_qubits, label=label, normalize=normalize), qubits
|
||||
StatePreparation(state, num_qubits, label=label, normalize=normalize),
|
||||
qubits,
|
||||
copy=False,
|
||||
)
|
||||
|
||||
def initialize(
|
||||
|
@ -4644,7 +4701,7 @@ class QuantumCircuit:
|
|||
|
||||
num_qubits = len(qubits) if isinstance(params, int) else None
|
||||
|
||||
return self.append(Initialize(params, num_qubits, normalize), qubits)
|
||||
return self.append(Initialize(params, num_qubits, normalize), qubits, copy=False)
|
||||
|
||||
def unitary(
|
||||
self,
|
||||
|
@ -4687,7 +4744,7 @@ class QuantumCircuit:
|
|||
if isinstance(qubits, (int, Qubit)) or len(qubits) > 1:
|
||||
qubits = [qubits]
|
||||
|
||||
return self.append(gate, qubits, [])
|
||||
return self.append(gate, qubits, [], copy=False)
|
||||
|
||||
def _current_scope(self) -> CircuitScopeInterface:
|
||||
if self._control_flow_scopes:
|
||||
|
@ -4869,7 +4926,7 @@ class QuantumCircuit:
|
|||
"When using 'while_loop' with a body, you must pass qubits and clbits."
|
||||
)
|
||||
|
||||
return self.append(WhileLoopOp(condition, body, label), qubits, clbits)
|
||||
return self.append(WhileLoopOp(condition, body, label), qubits, clbits, copy=False)
|
||||
|
||||
@typing.overload
|
||||
def for_loop(
|
||||
|
@ -4958,7 +5015,9 @@ class QuantumCircuit:
|
|||
"When using 'for_loop' with a body, you must pass qubits and clbits."
|
||||
)
|
||||
|
||||
return self.append(ForLoopOp(indexset, loop_parameter, body, label), qubits, clbits)
|
||||
return self.append(
|
||||
ForLoopOp(indexset, loop_parameter, body, label), qubits, clbits, copy=False
|
||||
)
|
||||
|
||||
@typing.overload
|
||||
def if_test(
|
||||
|
@ -5068,7 +5127,7 @@ class QuantumCircuit:
|
|||
elif qubits is None or clbits is None:
|
||||
raise CircuitError("When using 'if_test' with a body, you must pass qubits and clbits.")
|
||||
|
||||
return self.append(IfElseOp(condition, true_body, None, label), qubits, clbits)
|
||||
return self.append(IfElseOp(condition, true_body, None, label), qubits, clbits, copy=False)
|
||||
|
||||
def if_else(
|
||||
self,
|
||||
|
@ -5123,7 +5182,9 @@ class QuantumCircuit:
|
|||
else:
|
||||
condition = (circuit_scope.resolve_classical_resource(condition[0]), condition[1])
|
||||
|
||||
return self.append(IfElseOp(condition, true_body, false_body, label), qubits, clbits)
|
||||
return self.append(
|
||||
IfElseOp(condition, true_body, false_body, label), qubits, clbits, copy=False
|
||||
)
|
||||
|
||||
@typing.overload
|
||||
def switch(
|
||||
|
@ -5214,7 +5275,7 @@ class QuantumCircuit:
|
|||
|
||||
if qubits is None or clbits is None:
|
||||
raise CircuitError("When using 'switch' with cases, you must pass qubits and clbits.")
|
||||
return self.append(SwitchCaseOp(target, cases, label=label), qubits, clbits)
|
||||
return self.append(SwitchCaseOp(target, cases, label=label), qubits, clbits, copy=False)
|
||||
|
||||
def break_loop(self) -> InstructionSet:
|
||||
"""Apply :class:`~qiskit.circuit.BreakLoopOp`.
|
||||
|
@ -5240,8 +5301,10 @@ class QuantumCircuit:
|
|||
if self._control_flow_scopes:
|
||||
operation = BreakLoopPlaceholder()
|
||||
resources = operation.placeholder_resources()
|
||||
return self.append(operation, resources.qubits, resources.clbits)
|
||||
return self.append(BreakLoopOp(self.num_qubits, self.num_clbits), self.qubits, self.clbits)
|
||||
return self.append(operation, resources.qubits, resources.clbits, copy=False)
|
||||
return self.append(
|
||||
BreakLoopOp(self.num_qubits, self.num_clbits), self.qubits, self.clbits, copy=False
|
||||
)
|
||||
|
||||
def continue_loop(self) -> InstructionSet:
|
||||
"""Apply :class:`~qiskit.circuit.ContinueLoopOp`.
|
||||
|
@ -5267,9 +5330,9 @@ class QuantumCircuit:
|
|||
if self._control_flow_scopes:
|
||||
operation = ContinueLoopPlaceholder()
|
||||
resources = operation.placeholder_resources()
|
||||
return self.append(operation, resources.qubits, resources.clbits)
|
||||
return self.append(operation, resources.qubits, resources.clbits, copy=False)
|
||||
return self.append(
|
||||
ContinueLoopOp(self.num_qubits, self.num_clbits), self.qubits, self.clbits
|
||||
ContinueLoopOp(self.num_qubits, self.num_clbits), self.qubits, self.clbits, copy=False
|
||||
)
|
||||
|
||||
def add_calibration(
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
---
|
||||
features_circuits:
|
||||
- |
|
||||
:meth:`.QuantumCircuit.append` now has a ``copy`` keyword argument, which defaults to ``True``.
|
||||
When an instruction with runtime parameters (:class:`.ParameterExpression`\ s) is appended to
|
||||
a circuit, by default, the circuit has always created a copy of the instruction so that if
|
||||
:meth:`.QuantumCircuit.assign_parameters` attempts to mutate the instruction in place, it does
|
||||
not affect other references to the same instruction. Now, setting ``copy=False`` allows you to
|
||||
override this, so you can avoid the copy penalty if you know your instructions will not be used
|
||||
in other locations.
|
||||
- |
|
||||
:meth:`.QuantumCircuit.compose` now has a ``copy`` keyword argument, which defaults to ``True``.
|
||||
By default, :meth:`~.QuantumCircuit.compose` copies all instructions, so that mutations from one
|
||||
circuit do not affect any other. If ``copy=False``, then instructions from the other circuit
|
||||
will become directly owned by the new circuit, which may involve mutating them in place. The
|
||||
other circuit must not be used afterwards, in this case.
|
||||
fixes:
|
||||
- |
|
||||
:meth:`.QuantumCircuit.append` with ``copy=True`` (its default) will now correctly copy
|
||||
instructions parametrized by :class:`.ParameterExpression` instances, and not just by
|
||||
:class:`.Parameter` instances.
|
|
@ -345,6 +345,26 @@ class TestCircuitCompose(QiskitTestCase):
|
|||
|
||||
self.assertEqual(circuit_composed, circuit_expected)
|
||||
|
||||
def test_compose_copy(self):
|
||||
"""Test that `compose` copies instructions where appropriate."""
|
||||
base = QuantumCircuit(2, 2)
|
||||
|
||||
# If given a parametric instruction, the instruction should be copied in the output unless
|
||||
# specifically set to take ownership.
|
||||
parametric = QuantumCircuit(1)
|
||||
parametric.rz(Parameter("x"), 0)
|
||||
should_copy = base.compose(parametric, qubits=[0])
|
||||
self.assertIsNot(should_copy.data[-1].operation, parametric.data[-1].operation)
|
||||
self.assertEqual(should_copy.data[-1].operation, parametric.data[-1].operation)
|
||||
forbid_copy = base.compose(parametric, qubits=[0], copy=False)
|
||||
self.assertIs(forbid_copy.data[-1].operation, parametric.data[-1].operation)
|
||||
|
||||
conditional = QuantumCircuit(1, 1)
|
||||
conditional.x(0).c_if(conditional.clbits[0], True)
|
||||
test = base.compose(conditional, qubits=[0], clbits=[0], copy=False)
|
||||
self.assertIs(test.data[-1].operation, conditional.data[-1].operation)
|
||||
self.assertEqual(test.data[-1].operation.condition, (test.clbits[0], True))
|
||||
|
||||
def test_compose_classical(self):
|
||||
"""Composing on classical bits.
|
||||
|
||||
|
|
|
@ -145,6 +145,29 @@ class TestParameters(QiskitTestCase):
|
|||
qc.rx(param_a, 0)
|
||||
self.assertRaises(CircuitError, qc.rx, param_a_again, 0)
|
||||
|
||||
def test_append_copies_parametric(self):
|
||||
"""Test that `QuantumCircuit.append` copies instructions when they contain compile
|
||||
parameters and expressions."""
|
||||
param = Parameter("a")
|
||||
expr = param * 2
|
||||
gate_param = RZGate(param)
|
||||
gate_expr = RZGate(expr)
|
||||
|
||||
qc = QuantumCircuit(1)
|
||||
qc.append(gate_param, [0], copy=True)
|
||||
self.assertIsNot(qc.data[-1].operation, gate_param)
|
||||
self.assertEqual(qc.data[-1].operation, gate_param)
|
||||
|
||||
qc.append(gate_param, [0], copy=False)
|
||||
self.assertIs(qc.data[-1].operation, gate_param)
|
||||
|
||||
qc.append(gate_expr, [0], copy=True)
|
||||
self.assertIsNot(qc.data[-1].operation, gate_expr)
|
||||
self.assertEqual(qc.data[-1].operation, gate_expr)
|
||||
|
||||
qc.append(gate_expr, [0], copy=False)
|
||||
self.assertIs(qc.data[-1].operation, gate_expr)
|
||||
|
||||
def test_parameters_property(self):
|
||||
"""Test instantiating gate with variable parameters"""
|
||||
from qiskit.circuit.library.standard_gates.rx import RXGate
|
||||
|
|
Loading…
Reference in New Issue