Skip to content

Commit 33ac2e7

Browse files
New: Limited support for barriers (#12)
1 parent 4ce929e commit 33ac2e7

File tree

6 files changed

+138
-46
lines changed

6 files changed

+138
-46
lines changed

qiskit_rigetti/_qcs_backend.py

+1-14
Original file line numberDiff line numberDiff line change
@@ -13,32 +13,20 @@
1313
# See the License for the specific language governing permissions and
1414
# limitations under the License.
1515
##############################################################################
16-
import warnings
1716
from typing import Optional, Any, Union, List
1817
from uuid import uuid4
1918

2019
from pyquil import get_qc
2120
from pyquil.api import QuantumComputer, EngagementManager
2221
from qcs_api_client.client import QCSClientConfiguration
2322
from qiskit import QuantumCircuit, ClassicalRegister
24-
from qiskit.circuit import Barrier, Measure
23+
from qiskit.circuit import Measure
2524
from qiskit.providers import BackendV1, Options, Provider
2625
from qiskit.providers.models import QasmBackendConfiguration
2726

2827
from ._qcs_job import RigettiQCSJob
2928

3029

31-
def _remove_barriers(circuit: QuantumCircuit) -> None:
32-
"""Strips barriers from the circuit. Mutates the input circuit."""
33-
data = []
34-
for d in circuit.data:
35-
if isinstance(d[0], Barrier):
36-
warnings.warn("`barrier` has no effect on a RigettiQCSBackend and will be omitted")
37-
else:
38-
data.append(d)
39-
circuit.data = data
40-
41-
4230
def _prepare_readouts(circuit: QuantumCircuit) -> None:
4331
"""
4432
Errors if measuring into more than one readout. If only measuring one, ensures its name is 'ro'. Mutates the input
@@ -96,7 +84,6 @@ def _prepare_circuit(circuit: QuantumCircuit) -> QuantumCircuit:
9684
Returns a prepared copy of the circuit for execution on the QCS Backend.
9785
"""
9886
circuit = circuit.copy()
99-
_remove_barriers(circuit)
10087
_prepare_readouts(circuit)
10188
return circuit
10289

qiskit_rigetti/_qcs_job.py

+25
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
# See the License for the specific language governing permissions and
1414
# limitations under the License.
1515
##############################################################################
16+
import warnings
1617
from collections import Counter
1718
from datetime import datetime
1819
from typing import Optional, Dict, Any, List, Union, Iterator, cast
@@ -87,6 +88,7 @@ def _start(self) -> None:
8788
def _start_circuit(self, circuit: QuantumCircuit) -> Response:
8889
shots = self._options["shots"]
8990
qasm = circuit.qasm()
91+
qasm = self._handle_barriers(qasm, circuit.num_qubits)
9092

9193
before_compile: List[PreCompilationHook] = self._options.get("before_compile", [])
9294
for fn in before_compile:
@@ -107,6 +109,29 @@ def _start_circuit(self, circuit: QuantumCircuit) -> Response:
107109
# typing: QuantumComputer's inner QAM is generic, so we set the expected type here
108110
return cast(Response, self._qc.qam.execute(executable))
109111

112+
@staticmethod
113+
def _handle_barriers(qasm: str, num_circuit_qubits: int) -> str:
114+
lines = []
115+
for line in qasm.splitlines():
116+
if not line.startswith("barrier"):
117+
lines.append(line)
118+
continue
119+
120+
qubits = line[(len("barrier ")) :]
121+
num_qubits = qubits.count(",") + 1
122+
if num_qubits < num_circuit_qubits:
123+
warnings.warn(
124+
"barriers not applied to all circuit qubits will be omitted during execution on a RigettiQCSBackend"
125+
" -- apply barrier to all circuit qubits to preserve barrier effect",
126+
)
127+
continue
128+
129+
lines += [
130+
"#pragma PRESERVE_BLOCK;",
131+
"#pragma END_PRESERVE_BLOCK;",
132+
]
133+
return "\n".join(lines)
134+
110135
def result(self) -> Result:
111136
"""
112137
Wait until the job is complete, then return a result.

qiskit_rigetti/hooks/pre_compilation.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,6 @@ def set_rewiring(rewiring: str) -> PreCompilationHook:
3131
"""
3232

3333
def fn(qasm: str) -> str:
34-
return qasm.replace("OPENQASM 2.0;", f'OPENQASM 2.0;\n#pragma INITIAL_REWIRING "{rewiring}"')
34+
return qasm.replace("OPENQASM 2.0;", f'OPENQASM 2.0;\n#pragma INITIAL_REWIRING "{rewiring}";')
3535

3636
return fn

tests/test_hooks.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ def test_set_rewiring():
1818
assert new_qasm.rstrip() == "\n".join(
1919
[
2020
"OPENQASM 2.0;",
21-
'#pragma INITIAL_REWIRING "NAIVE"',
21+
'#pragma INITIAL_REWIRING "NAIVE";',
2222
'include "qelib1.inc";',
2323
]
2424
)

tests/test_qcs_backend.py

-19
Original file line numberDiff line numberDiff line change
@@ -57,25 +57,6 @@ def test_run__multiple_circuits(backend: RigettiQCSBackend):
5757
assert result.get_counts(1).keys() == {"000"}
5858

5959

60-
def test_run__barrier(backend: RigettiQCSBackend):
61-
circuit = make_circuit()
62-
circuit.barrier()
63-
qasm_before = circuit.qasm()
64-
65-
with pytest.warns(UserWarning, match="`barrier` has no effect on a RigettiQCSBackend and will be omitted"):
66-
job = execute(circuit, backend, shots=10)
67-
68-
assert circuit.qasm() == qasm_before, "should not modify original circuit"
69-
70-
assert job.backend() is backend
71-
result = job.result()
72-
assert job.status() == JobStatus.DONE
73-
assert result.backend_name == backend.configuration().backend_name
74-
assert result.results[0].header.name == circuit.name
75-
assert result.results[0].shots == 10
76-
assert result.get_counts().keys() == {"00"}
77-
78-
7960
def test_run__readout_register_not_named_ro(backend: RigettiQCSBackend):
8061
circuit = QuantumCircuit(QuantumRegister(2, "q"), ClassicalRegister(2, "not_ro"))
8162
circuit.measure([0, 1], [0, 1])

tests/test_qcs_job.py

+110-11
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,13 @@
2727

2828

2929
def test_init__start_circuit_unsuccessful(backend: RigettiQCSBackend):
30-
circuit = make_circuit(2 * backend.configuration().num_qubits) # Use too many qubits
30+
circuit = make_circuit(num_qubits=backend.configuration().num_qubits + 1) # Use too many qubits
3131
with pytest.raises(Exception):
3232
make_job(backend, circuit)
3333

3434

3535
def test_init__before_compile_hook(backend: RigettiQCSBackend, mocker: MockerFixture):
36-
circuit = make_circuit(backend.configuration().num_qubits)
36+
circuit = make_circuit(num_qubits=2)
3737
qc = get_qc(backend.configuration().backend_name)
3838
quil_to_native_quil_spy = mocker.spy(qc.compiler, "quil_to_native_quil")
3939

@@ -72,7 +72,7 @@ def before_compile_hook(qasm: str) -> str:
7272

7373

7474
def test_init__before_execute_hook(backend: RigettiQCSBackend, mocker: MockerFixture):
75-
circuit = make_circuit(backend.configuration().num_qubits)
75+
circuit = make_circuit(num_qubits=2)
7676
qc = get_qc(backend.configuration().backend_name)
7777
native_quil_to_executable_spy = mocker.spy(qc.compiler, "native_quil_to_executable")
7878

@@ -101,7 +101,7 @@ def before_execute_hook(quil: Program) -> Program:
101101

102102

103103
def test_init__ensure_native_quil__true(backend: RigettiQCSBackend, mocker: MockerFixture):
104-
circuit = make_circuit(backend.configuration().num_qubits)
104+
circuit = make_circuit(num_qubits=2)
105105
qc = get_qc(backend.configuration().backend_name)
106106
quil_to_native_quil_spy = mocker.spy(qc.compiler, "quil_to_native_quil")
107107

@@ -111,7 +111,7 @@ def test_init__ensure_native_quil__true(backend: RigettiQCSBackend, mocker: Mock
111111

112112

113113
def test_init__ensure_native_quil__ignored_if_no_pre_execution_hooks(backend: RigettiQCSBackend, mocker: MockerFixture):
114-
circuit = make_circuit(backend.configuration().num_qubits)
114+
circuit = make_circuit(num_qubits=2)
115115
qc = get_qc(backend.configuration().backend_name)
116116
quil_to_native_quil_spy = mocker.spy(qc.compiler, "quil_to_native_quil")
117117

@@ -121,7 +121,7 @@ def test_init__ensure_native_quil__ignored_if_no_pre_execution_hooks(backend: Ri
121121

122122

123123
def test_init__ensure_native_quil__false(backend: RigettiQCSBackend, mocker: MockerFixture):
124-
circuit = make_circuit(backend.configuration().num_qubits)
124+
circuit = make_circuit(num_qubits=2)
125125
qc = get_qc(backend.configuration().backend_name)
126126
quil_to_native_quil_spy = mocker.spy(qc.compiler, "quil_to_native_quil")
127127

@@ -131,7 +131,7 @@ def test_init__ensure_native_quil__false(backend: RigettiQCSBackend, mocker: Moc
131131

132132

133133
def test_init__ensure_native_quil__missing(backend: RigettiQCSBackend, mocker: MockerFixture):
134-
circuit = make_circuit(backend.configuration().num_qubits)
134+
circuit = make_circuit(num_qubits=2)
135135
qc = get_qc(backend.configuration().backend_name)
136136
quil_to_native_quil_spy = mocker.spy(qc.compiler, "quil_to_native_quil")
137137

@@ -140,14 +140,113 @@ def test_init__ensure_native_quil__missing(backend: RigettiQCSBackend, mocker: M
140140
assert quil_to_native_quil_spy.call_count == 1, "compile not performed correct number of times"
141141

142142

143+
def test_init__circuit_with_barrier__all_qubits(backend: RigettiQCSBackend, mocker: MockerFixture):
144+
circuit = QuilCircuit(QuantumRegister(2, "q"), ClassicalRegister(2, "ro"))
145+
circuit.h(0)
146+
circuit.barrier()
147+
circuit.h(0)
148+
circuit.measure([0, 1], [0, 1])
149+
qc = get_qc(backend.configuration().backend_name)
150+
quil_to_native_quil_spy = mocker.spy(qc.compiler, "quil_to_native_quil")
151+
152+
expected_qasm = "\n".join(
153+
[
154+
"OPENQASM 2.0;",
155+
'include "qelib1.inc";',
156+
"qreg q[2];",
157+
"creg ro[2];",
158+
"h q[0];",
159+
"#pragma PRESERVE_BLOCK;",
160+
"#pragma END_PRESERVE_BLOCK;",
161+
"h q[0];",
162+
"measure q[0] -> ro[0];",
163+
"measure q[1] -> ro[1];",
164+
]
165+
)
166+
167+
make_job(backend, circuit, qc)
168+
169+
program: Program = quil_to_native_quil_spy.call_args[0][0]
170+
qasm = program.out(calibrations=False).rstrip()
171+
assert qasm == expected_qasm
172+
173+
174+
def test_init__circuit_with_barrier__all_qubits_by_qreg(backend: RigettiQCSBackend, mocker: MockerFixture):
175+
qreg = QuantumRegister(2, "q")
176+
circuit = QuilCircuit(qreg, ClassicalRegister(2, "ro"))
177+
circuit.h(0)
178+
circuit.barrier(qreg)
179+
circuit.h(0)
180+
circuit.measure([0, 1], [0, 1])
181+
qc = get_qc(backend.configuration().backend_name)
182+
quil_to_native_quil_spy = mocker.spy(qc.compiler, "quil_to_native_quil")
183+
184+
expected_qasm = "\n".join(
185+
[
186+
"OPENQASM 2.0;",
187+
'include "qelib1.inc";',
188+
"qreg q[2];",
189+
"creg ro[2];",
190+
"h q[0];",
191+
"#pragma PRESERVE_BLOCK;",
192+
"#pragma END_PRESERVE_BLOCK;",
193+
"h q[0];",
194+
"measure q[0] -> ro[0];",
195+
"measure q[1] -> ro[1];",
196+
]
197+
)
198+
199+
make_job(backend, circuit, qc)
200+
201+
program: Program = quil_to_native_quil_spy.call_args[0][0]
202+
qasm = program.out(calibrations=False).rstrip()
203+
assert qasm == expected_qasm
204+
205+
206+
def test_init__circuit_with_barrier__qubit_subset(backend: RigettiQCSBackend, mocker: MockerFixture):
207+
circuit = QuilCircuit(QuantumRegister(2, "q"), ClassicalRegister(2, "ro"))
208+
circuit.h(0)
209+
circuit.barrier(0)
210+
circuit.h(0)
211+
circuit.measure([0, 1], [0, 1])
212+
qc = get_qc(backend.configuration().backend_name)
213+
quil_to_native_quil_spy = mocker.spy(qc.compiler, "quil_to_native_quil")
214+
215+
expected_qasm = "\n".join(
216+
[
217+
"OPENQASM 2.0;",
218+
'include "qelib1.inc";',
219+
"qreg q[2];",
220+
"creg ro[2];",
221+
"h q[0];",
222+
"h q[0];",
223+
"measure q[0] -> ro[0];",
224+
"measure q[1] -> ro[1];",
225+
]
226+
)
227+
228+
with pytest.warns(
229+
UserWarning,
230+
match=(
231+
"barriers not applied to all circuit qubits will be omitted during execution on a RigettiQCSBackend -- "
232+
"apply barrier to all circuit qubits to preserve barrier effect"
233+
),
234+
):
235+
make_job(backend, circuit, qc)
236+
237+
program: Program = quil_to_native_quil_spy.call_args[0][0]
238+
qasm = program.out(calibrations=False).rstrip()
239+
assert qasm == expected_qasm
240+
241+
143242
def test_result(job: RigettiQCSJob):
144243
result = job.result()
145244

146245
assert result.date == job.result().date, "Result not cached"
147246

148247
assert job.status() == JobStatus.DONE
149248

150-
assert result.backend_name == "2q-qvm"
249+
assert result.backend_name == "3q-qvm"
151250
assert result.job_id == job.job_id()
152251
assert result.success is True
153252

@@ -176,16 +275,16 @@ def test_submit(job: RigettiQCSJob):
176275

177276
@pytest.fixture
178277
def backend():
179-
return RigettiQCSProvider().get_simulator(num_qubits=2)
278+
return RigettiQCSProvider().get_simulator(num_qubits=3)
180279

181280

182281
@pytest.fixture
183282
def job(backend):
184-
circuit = make_circuit(backend.configuration().num_qubits)
283+
circuit = make_circuit(num_qubits=2)
185284
return make_job(backend, circuit)
186285

187286

188-
def make_circuit(num_qubits) -> QuilCircuit:
287+
def make_circuit(*, num_qubits) -> QuilCircuit:
189288
circuit = QuilCircuit(QuantumRegister(num_qubits, "q"), ClassicalRegister(num_qubits, "ro"))
190289
circuit.h(0)
191290
circuit.measure(range(num_qubits), range(num_qubits))

0 commit comments

Comments
 (0)