Source code for pennylane.noise.conditionals
# Copyright 2018-2024 Xanadu Quantum Technologies Inc.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Contains utility functions for building boolean conditionals for noise models
Developer note: Conditionals inherit from BooleanFn and store the condition they
utilize in the ``condition`` attribute.
"""
from inspect import isclass, signature
import pennylane as qml
from pennylane.boolean_fn import BooleanFn
from pennylane.measurements import MeasurementProcess, MeasurementValue, MidMeasureMP
from pennylane.operation import AnyWires
from pennylane.ops import Adjoint, Controlled
from pennylane.templates import ControlledSequence
from pennylane.wires import WireError, Wires
# pylint: disable = unnecessary-lambda, too-few-public-methods, too-many-statements, too-many-branches
[docs]class WiresIn(BooleanFn):
"""A conditional for evaluating if the wires of an operation exist in a specified set of wires.
Args:
wires (Union[Iterable[int, str], Wires]): Sequence of wires for building the wire set.
.. seealso:: Users are advised to use :func:`~.wires_in` for a functional construction.
"""
def __init__(self, wires):
self._cond = set(wires)
self.condition = self._cond
super().__init__(
lambda wire: _get_wires(wire).issubset(self._cond), f"WiresIn({list(wires)})"
)
[docs]class WiresEq(BooleanFn):
"""A conditional for evaluating if a given wire is equal to a specified set of wires.
Args:
wires (Union[Iterable[int, str], Wires]): Sequence of wires for building the wire set.
.. seealso:: Users are advised to use :func:`~.wires_eq` for a functional construction.
"""
def __init__(self, wires):
self._cond = set(wires)
self.condition = self._cond
super().__init__(
lambda wire: _get_wires(wire) == self._cond,
f"WiresEq({list(wires) if len(wires) > 1 else list(wires)[0]})",
)
def _get_wires(val):
"""Extract wires as a set from an integer, string, Iterable, Wires or Operation instance.
Args:
val (Union[int, str, Iterable, ~.wires.Wires, ~.operation.Operation]): object to be used
for building the wire set.
Returns:
set[Union[int, str]]: computed wire set
Raises:
ValueError: if the wire set cannot be computed for ``val``.
"""
iters = val if isinstance(val, (list, tuple, set, Wires)) else getattr(val, "wires", [val])
try:
wires = set().union(*((getattr(w, "wires", None) or Wires(w)).tolist() for w in iters))
except (TypeError, WireError):
raise ValueError(f"Wires cannot be computed for {val}") from None
return wires
[docs]def wires_in(wires):
"""Builds a conditional as a :class:`~.BooleanFn` for evaluating
if the wires of an input operation are within the specified set of wires.
Args:
wires (Union(Iterable[int, str], Wires, Operation, MeasurementProcess, int, str)):
Object to be used for building the wire set.
Returns:
:class:`WiresIn <pennylane.noise.conditionals.WiresIn>`: A callable object with
signature ``Union(Iterable[int, str], Wires, Operation, MeasurementProcess, int, str)``.
It evaluates to ``True`` if the wire set constructed from the input to the callable is
a subset of the one built from the specified ``wires`` set.
Raises:
ValueError: If the wire set cannot be computed from ``wires``.
**Example**
One may use ``wires_in`` with a given sequence of wires which are used as a wire set:
>>> cond_func = qml.noise.wires_in([0, 1])
>>> cond_func(qml.X(0))
True
>>> cond_func(qml.X(3))
False
Additionally, if an :class:`Operation <pennylane.operation.Operation>` is provided,
its ``wires`` are extracted and used to build the wire set:
>>> cond_func = qml.noise.wires_in(qml.CNOT(["alice", "bob"]))
>>> cond_func("alice")
True
>>> cond_func("eve")
False
"""
return WiresIn(_get_wires(wires))
[docs]def wires_eq(wires):
"""Builds a conditional as a :class:`~.BooleanFn` for evaluating
if a given wire is equal to specified set of wires.
Args:
wires (Union(Iterable[int, str], Wires, Operation, MeasurementProcess, int, str)):
Object to be used for building the wire set.
Returns:
:class:`WiresEq <pennylane.noise.conditionals.WiresEq>`: A callable object with
signature ``Union(Iterable[int, str], Wires, Operation, MeasurementProcess, int, str)``.
It evaluates to ``True`` if the wire set constructed from the input to the callable
is equal to the one built from the specified ``wires`` set.
Raises:
ValueError: If the wire set cannot be computed from ``wires``.
**Example**
One may use ``wires_eq`` with a given sequence of wires which are used as a wire set:
>>> cond_func = qml.noise.wires_eq(0)
>>> cond_func(qml.X(0))
True
>>> cond_func(qml.RY(1.23, wires=[3]))
False
Additionally, if an :class:`Operation <pennylane.operation.Operation>` is provided,
its ``wires`` are extracted and used to build the wire set:
>>> cond_func = qml.noise.wires_eq(qml.RX(1.0, "dino"))
>>> cond_func(qml.RZ(1.23, wires="dino"))
True
>>> cond_func("eve")
False
"""
return WiresEq(_get_wires(wires))
[docs]class OpIn(BooleanFn):
"""A conditional for evaluating if a given operation exist in a specified set of operations.
Args:
ops (Union[str, class, Operation, list[str, class, Operation]]): Sequence of operation
instances, string representations or classes to build the operation set.
.. seealso:: Users are advised to use :func:`~.op_in` for a functional construction.
"""
def __init__(self, ops):
ops_ = [ops] if not isinstance(ops, (list, tuple, set)) else ops
self._cond = [
(
op
if not isinstance(op, MeasurementProcess)
else (getattr(op, "obs", None) or getattr(op, "H", None))
)
for op in ops_
]
self._cops = _get_ops(ops)
self.condition = self._cops
super().__init__(
self._check_in_ops, f"OpIn({[getattr(op, '__name__', op) for op in self._cops]})"
)
def _check_in_ops(self, operation):
xs = operation if isinstance(operation, (list, tuple, set)) else [operation]
xs = [
(
op
if not isinstance(op, MeasurementProcess)
else (getattr(op, "obs", None) or getattr(op, "H", None))
)
for op in xs
]
cs = _get_ops(xs)
try:
return all(
(
c in self._cops
if isclass(x) or not getattr(x, "arithmetic_depth", 0)
else any(
(
_check_arithmetic_ops(op, x)
if isinstance(op, cp) and getattr(op, "arithmetic_depth", 0)
else cp == _get_ops(x)[0]
)
for op, cp in zip(self._cond, self._cops)
)
)
for x, c in zip(xs, cs)
)
except: # pylint: disable = bare-except # pragma: no cover
raise ValueError(
"OpIn does not support arithmetic operations "
"that cannot be converted to a linear combination"
) from None
[docs]class OpEq(BooleanFn):
"""A conditional for evaluating if a given operation is equal to the specified operation.
Args:
ops (Union[str, class, Operation]): An operation instance, string representation or
class to build the operation set.
.. seealso:: Users are advised to use :func:`~.op_eq` for a functional construction.
"""
def __init__(self, ops):
ops_ = [ops] if not isinstance(ops, (list, tuple, set)) else ops
self._cond = [
(
op
if not isinstance(op, MeasurementProcess)
else (getattr(op, "obs", None) or getattr(op, "H", None))
)
for op in ops_
]
self._cops = _get_ops(ops)
self.condition = self._cops
cops_names = list(getattr(op, "__name__", op) for op in self._cops)
super().__init__(
self._check_eq_ops,
f"OpEq({cops_names if len(cops_names) > 1 else cops_names[0]})",
)
def _check_eq_ops(self, operation):
if all(isclass(op) or not getattr(op, "arithmetic_depth", 0) for op in self._cond):
return _get_ops(operation) == self._cops
try:
xs = operation if isinstance(operation, (list, tuple, set)) else [operation]
xs = [
(
op
if not isinstance(op, MeasurementProcess)
else (getattr(op, "obs", None) or getattr(op, "H", None))
)
for op in xs
]
return (
len(xs) == len(self._cond)
and _get_ops(xs) == self._cops
and all(
_check_arithmetic_ops(op, x)
for (op, x) in zip(self._cond, xs)
if not isclass(x) and getattr(x, "arithmetic_depth", 0)
)
)
except: # pylint: disable = bare-except # pragma: no cover
raise ValueError(
"OpEq does not support arithmetic operations "
"that cannot be converted to a linear combination"
) from None
def _get_ops(val):
"""Computes the class for a given argument from its string name, instance,
or a sequence of them.
Args:
val (Union[str, Operation, Iterable]): object to be used
for building the operation set.
Returns:
tuple[class]: tuple of :class:`Operation <pennylane.operation.Operation>`
classes corresponding to val.
"""
vals = val if isinstance(val, (list, tuple, set)) else [val]
op_names = []
for _val in vals:
if isinstance(_val, str):
op_names.append(getattr(qml.ops, _val, None) or getattr(qml, _val))
elif isclass(_val) and not issubclass(_val, MeasurementProcess):
op_names.append(_val)
elif isinstance(_val, (MeasurementValue, MidMeasureMP)):
mid_measure = _val if isinstance(_val, MidMeasureMP) else _val.measurements[0]
op_names.append(["MidMeasure", "Reset"][getattr(mid_measure, "reset", 0)])
elif isinstance(_val, MeasurementProcess):
obs_name = _get_ops(getattr(_val, "obs", None) or getattr(_val, "H", None))
if len(obs_name) == 1:
obs_name = obs_name[0]
op_names.append(obs_name)
else:
op_names.append(getattr(_val, "__class__"))
return tuple(op_names)
def _check_arithmetic_ops(op1, op2):
"""Helper method for comparing two arithmetic operators based on type check of the bases"""
# pylint: disable = unnecessary-lambda-assignment
if isinstance(op1, (Adjoint, Controlled, ControlledSequence)) or isinstance(
op2, (Adjoint, Controlled, ControlledSequence)
):
return (
isinstance(op1, type(op2))
and op1.arithmetic_depth == op2.arithmetic_depth
and _get_ops(op1.base) == _get_ops(op2.base)
)
lc_cop = lambda op: qml.ops.LinearCombination(*op.terms())
if isinstance(op1, qml.ops.Exp) or isinstance(op2, qml.ops.Exp):
if (
not isinstance(op1, type(op2))
or (op1.base.arithmetic_depth != op2.base.arithmetic_depth)
or not qml.math.allclose(op1.coeff, op2.coeff)
or (op1.num_steps != op2.num_steps)
):
return False
if op1.base.arithmetic_depth:
return _check_arithmetic_ops(op1.base, op2.base)
return _get_ops(op1.base) == _get_ops(op2.base)
op1, op2 = qml.simplify(op1), qml.simplify(op2)
if op1.arithmetic_depth != op2.arithmetic_depth:
return False
coeffs, op_terms = lc_cop(op1).terms()
sprods = [_get_ops(getattr(op_term, "operands", op_term)) for op_term in op_terms]
def _lc_op(x):
coeffs2, op_terms2 = lc_cop(x).terms()
sprods2 = [_get_ops(getattr(op_term, "operands", op_term)) for op_term in op_terms2]
for coeff, sprod in zip(coeffs2, sprods2):
present, p_index = False, -1
while sprod in sprods[p_index + 1 :]:
p_index = sprods[p_index + 1 :].index(sprod) + (p_index + 1)
if qml.math.allclose(coeff, coeffs[p_index]):
coeffs.pop(p_index)
sprods.pop(p_index)
present = True
break
if not present:
break
return present
return _lc_op(op2)
[docs]def op_in(ops):
"""Builds a conditional as a :class:`~.BooleanFn` for evaluating
if a given operation exist in a specified set of operations.
Args:
ops (str, class, Operation, list(Union[str, class, Operation, MeasurementProcess])):
Sequence of string representations, instances, or classes of the operation(s).
Returns:
:class:`OpIn <pennylane.noise.conditionals.OpIn>`: A callable object that accepts
an :class:`~.Operation` or :class:`~.MeasurementProcess` and returns a boolean output.
For an input from: ``Union[str, class, Operation, list(Union[str, class, Operation])]``
and evaluates to ``True`` if the input operation(s) exists in the set of operation(s)
specified by ``ops``. For a ``MeasurementProcess`` input, similar evaluation happens
on its observable. In both cases, comparison is based on the operation's type,
irrespective of wires.
**Example**
One may use ``op_in`` with a string representation of the name of the operation:
>>> cond_func = qml.noise.op_in(["RX", "RY"])
>>> cond_func(qml.RX(1.23, wires=[0]))
True
>>> cond_func(qml.RZ(1.23, wires=[3]))
False
>>> cond_func([qml.RX(1.23, wires=[1]), qml.RY(4.56, wires=[2])])
True
Additionally, an instance of :class:`Operation <pennylane.operation.Operation>`
can also be provided:
>>> cond_func = qml.noise.op_in([qml.RX(1.0, "dino"), qml.RY(2.0, "rhino")])
>>> cond_func(qml.RX(1.23, wires=["eve"]))
True
>>> cond_func(qml.RY(1.23, wires=["dino"]))
True
>>> cond_func([qml.RX(1.23, wires=[1]), qml.RZ(4.56, wires=[2])])
False
"""
ops = [ops] if not isinstance(ops, (list, tuple, set)) else ops
return OpIn(ops)
[docs]def op_eq(ops):
"""Builds a conditional as a :class:`~.BooleanFn` for evaluating
if a given operation is equal to the specified operation.
Args:
ops (str, class, Operation, MeasurementProcess): String representation, an instance
or class of the operation, or a measurement process.
Returns:
:class:`OpEq <pennylane.noise.conditionals.OpEq>`: A callable object that accepts
an :class:`~.Operation` or :class:`~.MeasurementProcess` and returns a boolean output.
For an input from: ``Union[str, class, Operation]`` it evaluates to ``True``
if the input operations are equal to the operations specified by ``ops``.
For a ``MeasurementProcess`` input, similar evaluation happens on its observable. In
both cases, the comparison is based on the operation's type, irrespective of wires.
**Example**
One may use ``op_eq`` with a string representation of the name of the operation:
>>> cond_func = qml.noise.op_eq("RX")
>>> cond_func(qml.RX(1.23, wires=[0]))
True
>>> cond_func(qml.RZ(1.23, wires=[3]))
False
>>> cond_func("CNOT")
False
Additionally, an instance of :class:`Operation <pennylane.operation.Operation>`
can also be provided:
>>> cond_func = qml.noise.op_eq(qml.RX(1.0, "dino"))
>>> cond_func(qml.RX(1.23, wires=["eve"]))
True
>>> cond_func(qml.RY(1.23, wires=["dino"]))
False
"""
return OpEq(ops)
[docs]class MeasEq(qml.BooleanFn):
"""A conditional for evaluating if a given measurement process is equal to the specified measurement process.
Args:
mp(Union[Iterable[MeasurementProcess], MeasurementProcess, Callable]): A measurement process instance or
a measurement function to build the measurement set.
.. seealso:: Users are advised to use :func:`~.meas_eq` for a functional construction.
"""
def __init__(self, mps):
self._cond = [mps] if not isinstance(mps, (list, tuple, set)) else mps
self.condition, self._cmps = [], []
for mp in self._cond:
if (callable(mp) and (mp := _MEAS_FUNC_MAP.get(mp, None)) is None) or (
isclass(mp) and not issubclass(mp, MeasurementProcess)
):
raise ValueError(
f"MeasEq should be initialized with a MeasurementProcess, got {mp}"
)
self.condition.append(mp)
self._cmps.append(mp if isclass(mp) else mp.__class__)
mp_ops = list(getattr(op, "return_type", op.__class__.__name__) for op in self.condition)
mp_names = [
repr(op) if not isinstance(op, property) else repr(self.condition[idx].__name__)
for idx, op in enumerate(mp_ops)
]
super().__init__(
self._check_meas, f"MeasEq({mp_names if len(mp_names) > 1 else mp_names[0]})"
)
def _check_meas(self, mp):
if isclass(mp) and not issubclass(mp, MeasurementProcess):
return False
if callable(mp) and (mp := _MEAS_FUNC_MAP.get(mp, None)) is None:
return False
cmps = [
m_ if isclass(m_) else m_.__class__
for m_ in ([mp] if not isinstance(mp, (list, tuple, set)) else mp)
]
if len(cmps) != len(self._cond):
return False
return all(mp1 == mp2 for mp1, mp2 in zip(cmps, self._cmps))
[docs]def meas_eq(mps):
"""Builds a conditional as a :class:`~.BooleanFn` for evaluating
if a given measurement process is equal to the specified measurement process.
Args:
mps (MeasurementProcess, Callable): An instance(s) of any class that inherits from
:class:`~.MeasurementProcess` or a :mod:`measurement <pennylane.measurements>` function(s).
Returns:
:class:`MeasEq <pennylane.noise.conditionals.MeasEq>`: A callable object that accepts
an instance of :class:`~.MeasurementProcess` and returns a boolean output. It accepts
any input from: ``Union[class, function, list(Union[class, function, MeasurementProcess])]``
and evaluates to ``True`` if the input measurement process(es) is equal to the
measurement process(es) specified by ``ops``. Comparison is based on the measurement's
return type, irrespective of wires, observables or any other relevant attribute.
**Example**
One may use ``meas_eq`` with an instance of
:class:`MeasurementProcess <pennylane.operation.MeasurementProcess>`:
>>> cond_func = qml.noise.meas_eq(qml.expval(qml.Y(0)))
>>> cond_func(qml.expval(qml.Z(9)))
True
>>> cond_func(qml.sample(op=qml.Y(0)))
False
Additionally, a :mod:`measurement <pennylane.measurements>` function
can also be provided:
>>> cond_func = qml.noise.meas_eq(qml.expval)
>>> cond_func(qml.expval(qml.X(0)))
True
>>> cond_func(qml.probs(wires=[0, 1]))
False
>>> cond_func(qml.counts(qml.Z(0)))
False
"""
return MeasEq(mps)
_MEAS_FUNC_MAP = {
qml.expval: qml.measurements.ExpectationMP,
qml.var: qml.measurements.VarianceMP,
qml.state: qml.measurements.StateMP,
qml.density_matrix: qml.measurements.DensityMatrixMP,
qml.counts: qml.measurements.CountsMP,
qml.sample: qml.measurements.SampleMP,
qml.probs: qml.measurements.ProbabilityMP,
qml.vn_entropy: qml.measurements.VnEntropyMP,
qml.mutual_info: qml.measurements.MutualInfoMP,
qml.purity: qml.measurements.PurityMP,
qml.classical_shadow: qml.measurements.ClassicalShadowMP,
qml.shadow_expval: qml.measurements.ShadowExpvalMP,
qml.measure: qml.measurements.MidMeasureMP,
}
def _rename(newname):
"""Decorator function for renaming ``_partial`` function used in ``partial_wires``."""
def decorator(f):
f.__name__ = newname
return f
return decorator
def _process_instance(operation, *args, **kwargs):
"""Process an instance of a PennyLane operation to be used in ``partial_wires``."""
if args:
raise ValueError(
"Args cannot be provided when operation is an instance, "
f"got operation = {operation} and args = {args}."
)
op_class, op_type = type(operation), [] if kwargs else ["Mappable"]
if isinstance(operation, qml.measurements.MeasurementProcess):
op_type.append("MeasFunc")
elif isinstance(operation, (qml.ops.Adjoint, qml.ops.Controlled)):
op_type.append("MetaFunc")
args, metadata = getattr(operation, "_flatten")()
is_flat = "MeasFunc" in op_type or isinstance(operation, qml.ops.Controlled)
if len(metadata) > 1:
kwargs = {**dict(metadata[1] if not is_flat else metadata), **kwargs}
return op_class, op_type, args, kwargs
def _process_callable(operation):
"""Process a callable of PennyLane operation to be used in ``partial_wires``."""
_cmap = {qml.adjoint: qml.ops.Adjoint, qml.ctrl: qml.ops.Controlled}
if operation in _MEAS_FUNC_MAP:
return _MEAS_FUNC_MAP[operation], ["MeasFunc"]
if operation in [qml.adjoint, qml.ctrl]:
return _cmap[operation], ["MetaFunc"]
return operation, []
def _process_name(op_class, op_params, arg_params):
"""Obtain the name of the operation without its wires for `partial_wires` function."""
op_name = f"{op_class.__name__}("
for key, val in arg_params.copy().items():
if key in op_params:
op_name += f"{key}={val}, "
else: # pragma: no cover
del arg_params[key]
return op_name[:-2] + ")" if len(arg_params) else op_name[:-1]
[docs]def partial_wires(operation, *args, **kwargs):
"""Builds a partial function based on the given gate operation or measurement process
with all argument frozen except ``wires``.
Args:
operation (Operation | MeasurementProcess | class | Callable): Instance of an
operation or the class (callable) corresponding to the operation (measurement).
*args: Positional arguments provided in the case where the keyword argument
``operation`` is a class for building the partially evaluated instance.
**kwargs: Keyword arguments for the building the partially evaluated instance.
These will override any arguments present in the operation instance or ``args``.
Returns:
Callable: A wrapper function that accepts a sequence of wires as an argument or
any object with a ``wires`` property.
Raises:
ValueError: If ``args`` are provided when the given ``operation`` is an instance.
**Example**
One may give an instance of :class:`Operation <pennylane.operation.Operation>`
for the ``operation`` argument:
>>> func = qml.noise.partial_wires(qml.RX(1.2, [12]))
>>> func(2)
qml.RX(1.2, wires=[2])
>>> func(qml.RY(1.0, ["wires"]))
qml.RX(1.2, wires=["wires"])
Additionally, an :class:`Operation <pennylane.operation.Operation>` class can
also be provided, while providing required positional arguments via ``args``:
>>> func = qml.noise.partial_wires(qml.RX, 3.2, [20])
>>> func(qml.RY(1.0, [0]))
qml.RX(3.2, wires=[0])
Moreover, one can use ``kwargs`` instead of positional arguments:
>>> func = qml.noise.partial_wires(qml.RX, phi=1.2)
>>> func(qml.RY(1.0, [2]))
qml.RX(1.2, wires=[2])
>>> rfunc = qml.noise.partial_wires(qml.RX(1.2, [12]), phi=2.3)
>>> rfunc(qml.RY(1.0, ["light"]))
qml.RX(2.3, wires=["light"])
Finally, one may also use this with an instance of
:class:`MeasurementProcess <pennylane.measurement.MeasurementProcess>`
>>> func = qml.noise.partial_wires(qml.expval(qml.Z(0)))
>>> func(qml.RX(1.2, wires=[9]))
qml.expval(qml.Z(9))
"""
if callable(operation):
op_class, op_type = _process_callable(operation)
else:
op_class, op_type, args, kwargs = _process_instance(operation, *args, **kwargs)
# Developer Note: We use three TYPES to keep a track of PennyLane ``operation`` we have
# 1. "Mappable" -> We can use `map_wires` method of the `operation` with new wires.
# 2. "MeasFunc" -> We need to handle observable and/or wires for the measurement process.
# 3: "MetaFunc" -> We need to handle base operation for Adjoint or Controlled operation.
is_mappable = "Mappable" in op_type
if is_mappable:
op_type.remove("Mappable")
fsignature = signature(getattr(op_class, "__init__", op_class)).parameters
parameters = list(fsignature)[int("self" in fsignature) :]
arg_params = {**dict(zip(parameters, args)), **kwargs}
_fargs = {"MeasFunc": "obs", "MetaFunc": "base"}
if "op" in arg_params:
for key, val in _fargs.items():
if key in op_type:
arg_params[val] = arg_params.pop("op")
break
if op_class == qml.ops.Controlled and "control" in arg_params:
arg_params["control_wires"] = arg_params.pop("control")
arg_wires = arg_params.pop("wires", None)
op_name = _process_name(op_class, parameters, arg_params)
@_rename(op_name)
def _partial(wires=None, **partial_kwargs):
"""Wrapper function for partial_wires"""
op_args = arg_params
op_args["wires"] = wires or arg_wires
if wires is not None:
op_args["wires"] = getattr(wires, "wires", None) or (
[wires] if isinstance(wires, (int, str)) else list(wires)
)
if op_type:
_name, _op = _fargs[op_type[0]], "op"
if op_class == qml.measurements.ShadowExpvalMP:
_name = _op = "H"
if not op_args.get(_name, None) and partial_kwargs.get(_op, None):
obs = partial_kwargs.pop(_op, None)
if _name in parameters:
op_args[_name] = obs
if op_args["wires"] is None:
op_args["wires"] = obs.wires
if not is_mappable and (obs := op_args.get(_name, None)) and op_args["wires"]:
op_args[_name] = obs.map_wires(dict(zip(obs.wires, op_args["wires"])))
for key, val in op_args.items():
if key in parameters: # pragma: no cover
op_args[key] = val
if issubclass(op_class, qml.operation.Operation):
num_wires = getattr(op_class, "num_wires", AnyWires)
if "wires" in op_args and isinstance(num_wires, int):
if num_wires < len(op_args["wires"]) and num_wires == 1:
op_wires = op_args.pop("wires")
return tuple(operation(**op_args, wires=wire) for wire in op_wires)
if is_mappable and operation.wires is not None:
return operation.map_wires(dict(zip(operation.wires, op_args.pop("wires"))))
if "wires" not in parameters or (
"MeasFunc" in op_type and any(x in op_args for x in ["obs", "H"])
):
_ = op_args.pop("wires", None)
return op_class(**op_args)
return _partial
_modules/pennylane/noise/conditionals
Download Python script
Download Notebook
View on GitHub