Skip to content

Commit

Permalink
Fix Qiskit interop with v1.3 (#2050)
Browse files Browse the repository at this point in the history
Qiskit v1.3 changed the behavior of `num_qubits` on the `Target` class
in way that broke our `QirTarget` usage, since we expect to default to
`None` but the new logic ignores the `num_qubits` parameter to
`__init__` and always defaults to zero qubits, failing later calls to
transpilation. By shadowing the parent class property in `QirTarget` we
can continue returning `None` and trigger the right behavior in
transpilation while preserving a setter for use with v1.2.

Note: I tested locally with both v1.2.4 and v1.3.0 integration tests to
confirm the behavior is as expected.

Fixes #2047
  • Loading branch information
swernli authored Dec 4, 2024
1 parent 959ec84 commit e731e84
Show file tree
Hide file tree
Showing 7 changed files with 51 additions and 19 deletions.
2 changes: 1 addition & 1 deletion build.py
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,7 @@ def run_ci_historic_benchmark():
"ipykernel",
"nbconvert",
"pandas",
"qiskit>=1.2.2,<1.3.0",
"qiskit>=1.3.0,<2.0.0",
]
subprocess.run(pip_install_args, check=True, text=True, cwd=root_dir, env=pip_env)

Expand Down
30 changes: 19 additions & 11 deletions pip/qsharp/interop/qiskit/backends/backend_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from qiskit.circuit import (
QuantumCircuit,
)
from qiskit.version import get_version_info

from qiskit.qasm3.exporter import Exporter
from qiskit.providers import BackendV2, Options
Expand Down Expand Up @@ -319,19 +320,26 @@ def _transpile(self, circuit: QuantumCircuit, **options) -> QuantumCircuit:

circuit = self.run_qiskit_passes(circuit, options)

orig = self.target.num_qubits
try:
self.target.num_qubits = circuit.num_qubits
transpile_options = self._build_transpile_options(**options)
backend = transpile_options.pop("backend", self)
target = transpile_options.pop("target", self.target)
# in 1.3 add qubits_initially_zero=True to the transpile call
transpile_options = self._build_transpile_options(**options)
backend = transpile_options.pop("backend", self)
target = transpile_options.pop("target", self.target)
if get_version_info().startswith("1.2"):
# The older Qiskit version does not support the `qubits_initially_zero` option
transpiled_circuit = transpile(
circuit, backend=backend, target=target, **transpile_options
circuit,
backend=backend,
target=target,
**transpile_options,
)
return transpiled_circuit
finally:
self.target.num_qubits = orig
else:
transpiled_circuit = transpile(
circuit,
backend=backend,
target=target,
qubits_initially_zero=True,
**transpile_options,
)
return transpiled_circuit

def run_qiskit_passes(self, circuit, options):
pass_options = self._build_qiskit_pass_options(**options)
Expand Down
15 changes: 15 additions & 0 deletions pip/qsharp/interop/qiskit/backends/qirtarget.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ def __init__(
):
super().__init__(num_qubits=num_qubits)

self._num_qubits = num_qubits

if target_profile != TargetProfile.Base:
self.add_instruction(ControlFlowOp, name="control_flow")
self.add_instruction(IfElseOp, name="if_else")
Expand Down Expand Up @@ -121,3 +123,16 @@ def __init__(
self.add_instruction(IGate, name="id")

self.add_instruction(CHGate, name="ch")

# NOTE: The follow property intentionally shadows the property on the parent class to allow it to return `None`
# when the value is not set, which allows bypassing transpilation checks for number of qubits. Without this,
# versions of Qiskit 1.3.0 and higher default to `0` which will fail later checks.
@property
def num_qubits(self):
return self._num_qubits

# NOTE: The follow property setter intentionally shadows the property on the parent class to allow it to be set, which
# maintains compatiblity with Qiskit versions before 1.3.0 where the property was settable.
@num_qubits.setter
def num_qubits(self, value):
self._num_qubits = value
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,13 @@ def _test_circuit(
):
target_profile = TargetProfile.Base
seed = 42
backend = QSharpBackend(target_profile=target_profile, seed=seed)
backend = QSharpBackend(
target_profile=target_profile,
seed=seed,
transpile_options={
"optimization_level": 0 # Use no optimization to get consistent results in simulations
},
)
try:
job = backend.run(circuit, shots=num_shots)
result = job.result()
Expand Down
3 changes: 3 additions & 0 deletions pip/tests-integration/interop_qiskit/test_gateset_qasm.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ def run_transpile_test(
) -> None:
circuit = QuantumCircuit(3, 3)
operation(circuit)
if "optimization_level" not in options:
# Use no optimization so gate transpilation is consistent
options["optimization_level"] = 0
info = QSharpBackend()._qasm3(circuit, **options)
lines = info.splitlines()
# remove the first four lines, which are the header
Expand Down
10 changes: 5 additions & 5 deletions pip/tests-integration/interop_qiskit/test_re.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def test_qsharp_estimation_with_single_params() -> None:
for index in range(10):
circuit.t(index)
circuit.measure(index, index)
sim = ResourceEstimatorBackend()
sim = ResourceEstimatorBackend(transpile_options={"optimization_level": 0})
res = sim.run(circuit, params=params).result()

assert res["status"] == "success"
Expand Down Expand Up @@ -69,8 +69,8 @@ def test_estimate_qiskit_rgqft_multiplier() -> None:
{
"numQubits": 16,
"tCount": 90,
"rotationCount": 958,
"rotationDepth": 658,
"rotationCount": 1002,
"rotationDepth": 680,
"cczCount": 0,
"ccixCount": 0,
"measurementCount": 0,
Expand All @@ -94,8 +94,8 @@ def test_estimate_qiskit_rgqft_multiplier_in_threadpool() -> None:
{
"numQubits": 16,
"tCount": 90,
"rotationCount": 958,
"rotationDepth": 658,
"rotationCount": 1002,
"rotationDepth": 680,
"cczCount": 0,
"ccixCount": 0,
"measurementCount": 0,
Expand Down
2 changes: 1 addition & 1 deletion pip/tests-integration/test_requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
pytest==8.2.2
qiskit>=1.2.2,<1.3.0
qiskit>=1.3.0,<2.0.0
qirrunner==0.7.1
pyqir==0.10.2
qiskit-aer==0.14.2
Expand Down

0 comments on commit e731e84

Please sign in to comment.