Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add test for bug fix for issue #7335 #24

Closed
8 changes: 4 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions crates/circuit/src/operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,16 @@ impl StandardGate {
pub fn get_name(&self) -> &str {
self.name()
}

pub fn __eq__(&self, other: &Bound<PyAny>) -> Py<PyAny> {
let py = other.py();
let Ok(other) = other.extract::<Self>() else { return py.NotImplemented() };
(*self == other).into_py(py)
}

pub fn __hash__(&self) -> isize {
*self as isize
}
}

// This must be kept up-to-date with `StandardGate` when adding or removing
Expand Down
68 changes: 38 additions & 30 deletions qiskit/circuit/library/standard_gates/x.py
Original file line number Diff line number Diff line change
Expand Up @@ -952,8 +952,8 @@ class C4XGate(SingletonControlledGate):
of the relative phase version of c3x, the rc3x [2].

References:
[1] Barenco et al., 1995. https://arxiv.org/pdf/quant-ph/9503016.pdf
[2] Maslov, 2015. https://arxiv.org/abs/1508.03273
1. Barenco et al., 1995. https://arxiv.org/pdf/quant-ph/9503016.pdf
2. Maslov, 2015. https://arxiv.org/abs/1508.03273
"""

def __init__(
Expand Down Expand Up @@ -1320,9 +1320,14 @@ def _define(self):
class MCXRecursive(MCXGate):
"""Implement the multi-controlled X gate using recursion.

Using a single ancilla qubit, the multi-controlled X gate is recursively split onto
four sub-registers. This is done until we reach the 3- or 4-controlled X gate since
for these we have a concrete implementation that do not require ancillas.
Using a single clean ancilla qubit, the multi-controlled X gate is split into
four sub-registers, each one of them uses the V-chain method.

The method is based on Lemma 9 of [2], first shown in Lemma 7.3 of [1].

References:
1. Barenco et al., 1995. https://arxiv.org/pdf/quant-ph/9503016.pdf
2. Iten et al., 2015. https://arxiv.org/abs/1501.06911
"""

def __init__(
Expand Down Expand Up @@ -1378,32 +1383,35 @@ def _define(self):
qc._append(C4XGate(), q[:], [])
self.definition = qc
else:
for instr, qargs, cargs in self._recurse(q[:-1], q_ancilla=q[-1]):
qc._append(instr, qargs, cargs)
self.definition = qc
num_ctrl_qubits = len(q) - 1
q_ancilla = q[-1]
q_target = q[-2]
middle = ceil(num_ctrl_qubits / 2)
first_half = [*q[:middle]]
second_half = [*q[middle : num_ctrl_qubits - 1], q_ancilla]

qc._append(
MCXVChain(num_ctrl_qubits=len(first_half), dirty_ancillas=True),
qargs=[*first_half, q_ancilla, *q[middle : middle + len(first_half) - 2]],
cargs=[],
)
qc._append(
MCXVChain(num_ctrl_qubits=len(second_half), dirty_ancillas=True),
qargs=[*second_half, q_target, *q[: len(second_half) - 2]],
cargs=[],
)
qc._append(
MCXVChain(num_ctrl_qubits=len(first_half), dirty_ancillas=True),
qargs=[*first_half, q_ancilla, *q[middle : middle + len(first_half) - 2]],
cargs=[],
)
qc._append(
MCXVChain(num_ctrl_qubits=len(second_half), dirty_ancillas=True),
qargs=[*second_half, q_target, *q[: len(second_half) - 2]],
cargs=[],
)

def _recurse(self, q, q_ancilla=None):
# recursion stop
if len(q) == 4:
return [(C3XGate(), q[:], [])]
if len(q) == 5:
return [(C4XGate(), q[:], [])]
if len(q) < 4:
raise AttributeError("Something went wrong in the recursion, have less than 4 qubits.")

# recurse
num_ctrl_qubits = len(q) - 1
middle = ceil(num_ctrl_qubits / 2)
first_half = [*q[:middle], q_ancilla]
second_half = [*q[middle:num_ctrl_qubits], q_ancilla, q[num_ctrl_qubits]]

rule = []
rule += self._recurse(first_half, q_ancilla=q[middle])
rule += self._recurse(second_half, q_ancilla=q[middle - 1])
rule += self._recurse(first_half, q_ancilla=q[middle])
rule += self._recurse(second_half, q_ancilla=q[middle - 1])

return rule
self.definition = qc


class MCXVChain(MCXGate):
Expand Down
26 changes: 22 additions & 4 deletions qiskit/primitives/backend_estimator_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ class _PreprocessedData:


class BackendEstimatorV2(BaseEstimatorV2):
"""Evaluates expectation values for provided quantum circuit and observable combinations
r"""Evaluates expectation values for provided quantum circuit and observable combinations.

The :class:`~.BackendEstimatorV2` class is a generic implementation of the
:class:`~.BaseEstimatorV2` interface that is used to wrap a :class:`~.BackendV2`
Expand All @@ -87,7 +87,19 @@ class BackendEstimatorV2(BaseEstimatorV2):
precludes doing any provider- or backend-specific optimizations.

This class does not perform any measurement or gate mitigation, and, presently, is only
compatible with Pauli-based observables.
compatible with Pauli-based observables. More formally, given an observable of the type
:math:`O=\sum_{i=1}^Na_iP_i`, where :math:`a_i` is a complex number and :math:`P_i` is a
Pauli operator, the estimator calculates the expectation :math:`\mathbb{E}(P_i)` of each
:math:`P_i` and finally calculates the expectation value of :math:`O` as
:math:`\mathbb{E}(O)=\sum_{i=1}^Na_i\mathbb{E}(P_i)`. The reported ``std`` is calculated
as

.. math::

\frac{\sum_{i=1}^{n}|a_i|\sqrt{\textrm{Var}\big(P_i\big)}}{\sqrt{N}}\:,

where :math:`\textrm{Var}(P_i)` is the variance of :math:`P_i`, :math:`N=O(\epsilon^{-2})` is
the number of shots, and :math:`\epsilon` is the target precision [1].

Each tuple of ``(circuit, observables, <optional> parameter values, <optional> precision)``,
called an estimator primitive unified bloc (PUB), produces its own array-based result. The
Expand All @@ -104,6 +116,12 @@ class BackendEstimatorV2(BaseEstimatorV2):

* ``seed_simulator``: The seed to use in the simulator. If None, a random seed will be used.
Default: None.

**Reference:**

[1] O. Crawford, B. van Straaten, D. Wang, T. Parks, E. Campbell, St. Brierley,
Efficient quantum measurement of Pauli operators in the presence of finite sampling error.
`Quantum 5, 385 <https://doi.org/10.22331/q-2021-01-20-385>`_
"""

def __init__(
Expand Down Expand Up @@ -254,8 +272,8 @@ def _postprocess_pub(
for pauli, coeff in bc_obs[index].items():
expval, variance = expval_map[param_index, pauli]
evs[index] += expval * coeff
variances[index] += variance * coeff**2
stds = np.sqrt(variances / shots)
variances[index] += np.abs(coeff) * variance**0.5
stds = variances / np.sqrt(shots)
data_bin = DataBin(evs=evs, stds=stds, shape=evs.shape)
return PubResult(data_bin, metadata={"target_precision": pub.precision})

Expand Down
32 changes: 8 additions & 24 deletions qiskit/qasm3/ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -456,39 +456,23 @@ class SubroutineBlock(ProgramBlock):
pass


class QuantumArgument(QuantumDeclaration):
"""
quantumArgument
: 'qreg' Identifier designator? | 'qubit' designator? Identifier
"""


class QuantumGateSignature(ASTNode):
class QuantumGateDefinition(Statement):
"""
quantumGateSignature
: quantumGateName ( LPAREN identifierList? RPAREN )? identifierList
quantumGateDefinition
: 'gate' quantumGateSignature quantumBlock
"""

def __init__(
self,
name: Identifier,
qargList: List[Identifier],
params: Optional[List[Expression]] = None,
params: Tuple[Identifier, ...],
qubits: Tuple[Identifier, ...],
body: QuantumBlock,
):
self.name = name
self.qargList = qargList
self.params = params


class QuantumGateDefinition(Statement):
"""
quantumGateDefinition
: 'gate' quantumGateSignature quantumBlock
"""

def __init__(self, quantumGateSignature: QuantumGateSignature, quantumBlock: QuantumBlock):
self.quantumGateSignature = quantumGateSignature
self.quantumBlock = quantumBlock
self.qubits = qubits
self.body = body


class SubroutineDefinition(Statement):
Expand Down
Loading