Skip to content
19 changes: 17 additions & 2 deletions qiskit/primitives/base/base_sampler.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@
from copy import copy
from typing import Generic, TypeVar

from qiskit.circuit import QuantumCircuit
from qiskit.circuit import ControlFlowOp, Measure, QuantumCircuit
from qiskit.circuit.parametertable import ParameterView
from qiskit.providers import JobV1 as Job

Expand Down Expand Up @@ -159,7 +159,6 @@ def _run(
) -> T:
raise NotImplementedError("The subclass of BaseSampler must implment `_run` method.")

# TODO: validate measurement gates are present
@classmethod
def _validate_circuits(
cls,
Expand All @@ -173,6 +172,11 @@ def _validate_circuits(
"Sampler requires classical bits, plus measurements "
"on the desired qubits."
)
if not _has_measure(circuit):
raise ValueError(
f"The {i}-th circuit does not have Measure instruction. "
"Without measurements, the circuit cannot be sampled from."
)
return circuits

@property
Expand All @@ -192,3 +196,14 @@ def parameters(self) -> tuple[ParameterView, ...]:
List of the parameters in each quantum circuit.
"""
return tuple(self._parameters)


def _has_measure(circuit: QuantumCircuit) -> bool:
for instruction in circuit:
Comment thread
t-imamichi marked this conversation as resolved.
Outdated
if isinstance(instruction.operation, Measure):
return True
elif isinstance(instruction.operation, ControlFlowOp):
Comment thread
Cryoris marked this conversation as resolved.
for block in instruction.operation.blocks:
if _has_measure(block):
return True
return False
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
fixes:
- |
Add early validation for :class:`.BaseSampler` when input :class:`.QuantumCircuit` does not
have measurements.
11 changes: 7 additions & 4 deletions test/python/primitives/test_sampler.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
from qiskit import QuantumCircuit
from qiskit.circuit import Parameter
from qiskit.circuit.library import RealAmplitudes
from qiskit.exceptions import QiskitError
from qiskit.extensions.unitary import UnitaryGate
from qiskit.primitives import Sampler, SamplerResult
from qiskit.providers import JobStatus, JobV1
Expand Down Expand Up @@ -276,6 +275,9 @@ def test_run_errors(self):
qc2.measure_all()
qc3 = QuantumCircuit(1)
qc4 = QuantumCircuit(1, 1)
qc5 = QuantumCircuit(1, 1)
with qc5.for_loop(range(5)):
qc5.h(0)

sampler = Sampler()
with self.subTest("set parameter values to a non-parameterized circuit"):
Expand All @@ -294,10 +296,11 @@ def test_run_errors(self):
with self.assertRaises(ValueError):
_ = sampler.run([qc3], [[]])
with self.subTest("no measurement"):
with self.assertRaises(QiskitError):
# The following raises QiskitError because this check is located in
# `Sampler._preprocess_circuit`
with self.assertRaises(ValueError):
_ = sampler.run([qc4], [[]])
with self.subTest("no measurement in control flow"):
with self.assertRaises(ValueError):
_ = sampler.run([qc5], [[]])

def test_run_empty_parameter(self):
"""Test for empty parameter"""
Expand Down