Skip to content

🎨 Re-implemented AE without Qiskit Application Modules #506

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

Merged
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
81 changes: 34 additions & 47 deletions src/mqt/bench/benchmarks/ae.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,66 +4,53 @@

import numpy as np
from qiskit import QuantumCircuit
from qiskit_algorithms import AmplitudeEstimation, EstimationProblem
from qiskit.circuit.library import PhaseEstimation


def create_circuit(num_qubits: int) -> QuantumCircuit:
def create_circuit(num_qubits: int, probability: float = 0.2) -> QuantumCircuit:
"""Returns a quantum circuit implementing Quantum Amplitude Estimation.

Arguments:
num_qubits: number of qubits of the returned quantum circuit
"""
ae = AmplitudeEstimation(
num_eval_qubits=num_qubits - 1, # -1 because of the to be estimated qubit
)
problem = get_estimation_problem()

qc = ae.construct_circuit(problem)
qc.name = "ae"
qc.measure_all()

return qc
num_qubits: Total number of qubits, including evaluation and target qubits.
probability: Probability of the "good" state.

Returns:
QuantumCircuit: The constructed amplitude estimation circuit.
"""
if num_qubits < 2:
msg = "Number of qubits must be at least 2 (1 evaluation + 1 target)."
raise ValueError(msg)

class BernoulliQ(QuantumCircuit): # type: ignore[misc]
"""A circuit representing the Bernoulli Q operator."""

def __init__(self, probability: float) -> None:
"""Initialize the Bernoulli Q operator."""
super().__init__(1) # circuit on 1 qubit
# Compute the rotation angle: theta_p = 2 * arcsin(sqrt(p))
theta_p = 2 * np.arcsin(np.sqrt(probability))

self._theta_p = 2 * np.arcsin(np.sqrt(probability))
self.ry(2 * self._theta_p, 0)
# Create the state-preparation operator (A operator) as a single-qubit circuit.
state_preparation = QuantumCircuit(1)
state_preparation.ry(theta_p, 0)

def __eq__(self, other: object) -> bool:
"""Return if the operators are equal."""
return isinstance(other, BernoulliQ) and self._theta_p == other._theta_p
# Create the Grover operator (Bernoulli Q operator) as a single-qubit circuit.
# It applies a rotation of 2 * theta_p, which corresponds to an effective rotation of 4*arcsin(sqrt(p)).
grover_operator = QuantumCircuit(1)
grover_operator.ry(2 * theta_p, 0)

def __hash__(self) -> int:
"""Return a hash of the operator."""
return hash(self._theta_p)
# Compute the number of evaluation qubits (phase estimation qubits).
num_eval_qubits = num_qubits - 1

def power(self, power: float, _matrix_power: bool = True) -> QuantumCircuit:
"""Return a circuit implementing the power of the operator."""
q_k = QuantumCircuit(1)
q_k.ry(2 * power * self._theta_p, 0)
return q_k
# Build the phase estimation circuit with the specified number of evaluation qubits and the Grover operator.
pe = PhaseEstimation(num_eval_qubits, grover_operator)

# Create the overall circuit using the quantum registers from the phase estimation circuit.
qc = QuantumCircuit(*pe.qregs)

def get_estimation_problem() -> EstimationProblem:
"""Returns a estimation problem instance for a fixed p value."""
p = 0.2
# Compose the state-preparation operator on the target qubit. We assume that the target qubit
# is the last qubit in the register list.
qc.compose(state_preparation, list(range(num_eval_qubits, qc.num_qubits)), inplace=True)

"""A circuit representing the Bernoulli A operator."""
a = QuantumCircuit(1)
theta_p = 2 * np.arcsin(np.sqrt(p))
a.ry(theta_p, 0)
# Compose the phase estimation component.
qc.compose(pe, inplace=True)

"""A circuit representing the Bernoulli Q operator."""
q = BernoulliQ(p)
# Name the circuit and add measurements to all evaluation qubits.
qc.name = "ae"
qc.measure_all()

return EstimationProblem(
state_preparation=a, # A operator
grover_operator=q, # Q operator
objective_qubits=[0], # the "good" state Psi1 is identified as measuring |1> in qubit 0
)
return qc
6 changes: 6 additions & 0 deletions tests/test_bench.py
Original file line number Diff line number Diff line change
Expand Up @@ -1076,3 +1076,9 @@ def test_tket_mapped_circuit_qubit_number() -> None:
)
assert isinstance(res, pytket.Circuit)
assert res.n_qubits == 127


def test_create_ae_circuit_with_invalid_qubit_number() -> None:
"""Testing the minimum number of qubits in the amplitude estimation circuit."""
with pytest.raises(ValueError, match=r"Number of qubits must be at least 2 \(1 evaluation \+ 1 target\)."):
get_benchmark("ae", 1, 1)
Loading