Skip to content

Commit

Permalink
Improve performance of Estimator and Sampler by avoiding deep cop…
Browse files Browse the repository at this point in the history
…y at `circuit_to_instruction`. (#8403)

* add simplified circuit_to_instruction

* add a safe guard

* optimize sampler as well as estimator

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
  • Loading branch information
t-imamichi and mergify[bot] authored Aug 26, 2022
1 parent c008008 commit 9f5f8fb
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 5 deletions.
4 changes: 2 additions & 2 deletions qiskit/primitives/estimator.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
from .base_estimator import BaseEstimator
from .estimator_result import EstimatorResult
from .primitive_job import PrimitiveJob
from .utils import init_circuit, init_observable
from .utils import bound_circuit_to_instruction, init_circuit, init_observable


class Estimator(BaseEstimator):
Expand Down Expand Up @@ -114,7 +114,7 @@ def _call(
f"The number of qubits of a circuit ({circ.num_qubits}) does not match "
f"the number of qubits of a observable ({obs.num_qubits})."
)
final_state = Statevector(circ)
final_state = Statevector(bound_circuit_to_instruction(circ))
expectation_value = final_state.expectation_value(obs)
if shots is None:
expectation_values.append(expectation_value)
Expand Down
4 changes: 2 additions & 2 deletions qiskit/primitives/sampler.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
from .base_sampler import BaseSampler
from .primitive_job import PrimitiveJob
from .sampler_result import SamplerResult
from .utils import final_measurement_mapping, init_circuit
from .utils import bound_circuit_to_instruction, final_measurement_mapping, init_circuit


class Sampler(BaseSampler):
Expand Down Expand Up @@ -113,7 +113,7 @@ def _call(
)
qargs_list.append(self._qargs_list[i])
probabilities = [
Statevector(circ).probabilities(qargs=qargs)
Statevector(bound_circuit_to_instruction(circ)).probabilities(qargs=qargs)
for circ, qargs in zip(bound_circuits, qargs_list)
]
if shots is not None:
Expand Down
34 changes: 33 additions & 1 deletion qiskit/primitives/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

from __future__ import annotations

from qiskit.circuit import ParameterExpression, QuantumCircuit
from qiskit.circuit import ParameterExpression, QuantumCircuit, Instruction
from qiskit.extensions.quantum_initializer.initializer import Initialize
from qiskit.opflow import PauliSumOp
from qiskit.quantum_info import SparsePauliOp, Statevector
Expand Down Expand Up @@ -111,3 +111,35 @@ def final_measurement_mapping(circuit: QuantumCircuit) -> dict[int, int]:
# Sort so that classical bits are in numeric order low->high.
mapping = dict(sorted(mapping.items(), key=lambda item: item[1]))
return mapping


def bound_circuit_to_instruction(circuit: QuantumCircuit) -> Instruction:
"""Build an :class:`~qiskit.circuit.Instruction` object from
a :class:`~qiskit.circuit.QuantumCircuit`
This is a specialized version of :func:`~qiskit.converters.circuit_to_instruction`
to avoid deep copy. This requires a quantum circuit whose parameters are all bound.
Because this does not take a copy of the input circuit, this assumes that the input
circuit won't be modified.
If https://github.com/Qiskit/qiskit-terra/issues/7983 is resolved,
we can remove this function.
Args:
circuit(QuantumCircuit): Input quantum circuit
Returns:
An :class:`~qiskit.circuit.Instruction` object
"""
if len(circuit.qregs) > 1:
return circuit.to_instruction()

# len(circuit.qregs) == 1 -> No need to flatten qregs
inst = Instruction(
name=circuit.name,
num_qubits=circuit.num_qubits,
num_clbits=circuit.num_clbits,
params=[],
)
inst.definition = circuit
return inst

0 comments on commit 9f5f8fb

Please sign in to comment.