Skip to content

Commit

Permalink
New: Limited support for barriers (#12)
Browse files Browse the repository at this point in the history
  • Loading branch information
ameyer-rigetti authored Jul 26, 2021
1 parent 4ce929e commit 33ac2e7
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 46 deletions.
15 changes: 1 addition & 14 deletions qiskit_rigetti/_qcs_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,32 +13,20 @@
# See the License for the specific language governing permissions and
# limitations under the License.
##############################################################################
import warnings
from typing import Optional, Any, Union, List
from uuid import uuid4

from pyquil import get_qc
from pyquil.api import QuantumComputer, EngagementManager
from qcs_api_client.client import QCSClientConfiguration
from qiskit import QuantumCircuit, ClassicalRegister
from qiskit.circuit import Barrier, Measure
from qiskit.circuit import Measure
from qiskit.providers import BackendV1, Options, Provider
from qiskit.providers.models import QasmBackendConfiguration

from ._qcs_job import RigettiQCSJob


def _remove_barriers(circuit: QuantumCircuit) -> None:
"""Strips barriers from the circuit. Mutates the input circuit."""
data = []
for d in circuit.data:
if isinstance(d[0], Barrier):
warnings.warn("`barrier` has no effect on a RigettiQCSBackend and will be omitted")
else:
data.append(d)
circuit.data = data


def _prepare_readouts(circuit: QuantumCircuit) -> None:
"""
Errors if measuring into more than one readout. If only measuring one, ensures its name is 'ro'. Mutates the input
Expand Down Expand Up @@ -96,7 +84,6 @@ def _prepare_circuit(circuit: QuantumCircuit) -> QuantumCircuit:
Returns a prepared copy of the circuit for execution on the QCS Backend.
"""
circuit = circuit.copy()
_remove_barriers(circuit)
_prepare_readouts(circuit)
return circuit

Expand Down
25 changes: 25 additions & 0 deletions qiskit_rigetti/_qcs_job.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
##############################################################################
import warnings
from collections import Counter
from datetime import datetime
from typing import Optional, Dict, Any, List, Union, Iterator, cast
Expand Down Expand Up @@ -87,6 +88,7 @@ def _start(self) -> None:
def _start_circuit(self, circuit: QuantumCircuit) -> Response:
shots = self._options["shots"]
qasm = circuit.qasm()
qasm = self._handle_barriers(qasm, circuit.num_qubits)

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

@staticmethod
def _handle_barriers(qasm: str, num_circuit_qubits: int) -> str:
lines = []
for line in qasm.splitlines():
if not line.startswith("barrier"):
lines.append(line)
continue

qubits = line[(len("barrier ")) :]
num_qubits = qubits.count(",") + 1
if num_qubits < num_circuit_qubits:
warnings.warn(
"barriers not applied to all circuit qubits will be omitted during execution on a RigettiQCSBackend"
" -- apply barrier to all circuit qubits to preserve barrier effect",
)
continue

lines += [
"#pragma PRESERVE_BLOCK;",
"#pragma END_PRESERVE_BLOCK;",
]
return "\n".join(lines)

def result(self) -> Result:
"""
Wait until the job is complete, then return a result.
Expand Down
2 changes: 1 addition & 1 deletion qiskit_rigetti/hooks/pre_compilation.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,6 @@ def set_rewiring(rewiring: str) -> PreCompilationHook:
"""

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

return fn
2 changes: 1 addition & 1 deletion tests/test_hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def test_set_rewiring():
assert new_qasm.rstrip() == "\n".join(
[
"OPENQASM 2.0;",
'#pragma INITIAL_REWIRING "NAIVE"',
'#pragma INITIAL_REWIRING "NAIVE";',
'include "qelib1.inc";',
]
)
Expand Down
19 changes: 0 additions & 19 deletions tests/test_qcs_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,25 +57,6 @@ def test_run__multiple_circuits(backend: RigettiQCSBackend):
assert result.get_counts(1).keys() == {"000"}


def test_run__barrier(backend: RigettiQCSBackend):
circuit = make_circuit()
circuit.barrier()
qasm_before = circuit.qasm()

with pytest.warns(UserWarning, match="`barrier` has no effect on a RigettiQCSBackend and will be omitted"):
job = execute(circuit, backend, shots=10)

assert circuit.qasm() == qasm_before, "should not modify original circuit"

assert job.backend() is backend
result = job.result()
assert job.status() == JobStatus.DONE
assert result.backend_name == backend.configuration().backend_name
assert result.results[0].header.name == circuit.name
assert result.results[0].shots == 10
assert result.get_counts().keys() == {"00"}


def test_run__readout_register_not_named_ro(backend: RigettiQCSBackend):
circuit = QuantumCircuit(QuantumRegister(2, "q"), ClassicalRegister(2, "not_ro"))
circuit.measure([0, 1], [0, 1])
Expand Down
121 changes: 110 additions & 11 deletions tests/test_qcs_job.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,13 @@


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


def test_init__before_compile_hook(backend: RigettiQCSBackend, mocker: MockerFixture):
circuit = make_circuit(backend.configuration().num_qubits)
circuit = make_circuit(num_qubits=2)
qc = get_qc(backend.configuration().backend_name)
quil_to_native_quil_spy = mocker.spy(qc.compiler, "quil_to_native_quil")

Expand Down Expand Up @@ -72,7 +72,7 @@ def before_compile_hook(qasm: str) -> str:


def test_init__before_execute_hook(backend: RigettiQCSBackend, mocker: MockerFixture):
circuit = make_circuit(backend.configuration().num_qubits)
circuit = make_circuit(num_qubits=2)
qc = get_qc(backend.configuration().backend_name)
native_quil_to_executable_spy = mocker.spy(qc.compiler, "native_quil_to_executable")

Expand Down Expand Up @@ -101,7 +101,7 @@ def before_execute_hook(quil: Program) -> Program:


def test_init__ensure_native_quil__true(backend: RigettiQCSBackend, mocker: MockerFixture):
circuit = make_circuit(backend.configuration().num_qubits)
circuit = make_circuit(num_qubits=2)
qc = get_qc(backend.configuration().backend_name)
quil_to_native_quil_spy = mocker.spy(qc.compiler, "quil_to_native_quil")

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


def test_init__ensure_native_quil__ignored_if_no_pre_execution_hooks(backend: RigettiQCSBackend, mocker: MockerFixture):
circuit = make_circuit(backend.configuration().num_qubits)
circuit = make_circuit(num_qubits=2)
qc = get_qc(backend.configuration().backend_name)
quil_to_native_quil_spy = mocker.spy(qc.compiler, "quil_to_native_quil")

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


def test_init__ensure_native_quil__false(backend: RigettiQCSBackend, mocker: MockerFixture):
circuit = make_circuit(backend.configuration().num_qubits)
circuit = make_circuit(num_qubits=2)
qc = get_qc(backend.configuration().backend_name)
quil_to_native_quil_spy = mocker.spy(qc.compiler, "quil_to_native_quil")

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


def test_init__ensure_native_quil__missing(backend: RigettiQCSBackend, mocker: MockerFixture):
circuit = make_circuit(backend.configuration().num_qubits)
circuit = make_circuit(num_qubits=2)
qc = get_qc(backend.configuration().backend_name)
quil_to_native_quil_spy = mocker.spy(qc.compiler, "quil_to_native_quil")

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


def test_init__circuit_with_barrier__all_qubits(backend: RigettiQCSBackend, mocker: MockerFixture):
circuit = QuilCircuit(QuantumRegister(2, "q"), ClassicalRegister(2, "ro"))
circuit.h(0)
circuit.barrier()
circuit.h(0)
circuit.measure([0, 1], [0, 1])
qc = get_qc(backend.configuration().backend_name)
quil_to_native_quil_spy = mocker.spy(qc.compiler, "quil_to_native_quil")

expected_qasm = "\n".join(
[
"OPENQASM 2.0;",
'include "qelib1.inc";',
"qreg q[2];",
"creg ro[2];",
"h q[0];",
"#pragma PRESERVE_BLOCK;",
"#pragma END_PRESERVE_BLOCK;",
"h q[0];",
"measure q[0] -> ro[0];",
"measure q[1] -> ro[1];",
]
)

make_job(backend, circuit, qc)

program: Program = quil_to_native_quil_spy.call_args[0][0]
qasm = program.out(calibrations=False).rstrip()
assert qasm == expected_qasm


def test_init__circuit_with_barrier__all_qubits_by_qreg(backend: RigettiQCSBackend, mocker: MockerFixture):
qreg = QuantumRegister(2, "q")
circuit = QuilCircuit(qreg, ClassicalRegister(2, "ro"))
circuit.h(0)
circuit.barrier(qreg)
circuit.h(0)
circuit.measure([0, 1], [0, 1])
qc = get_qc(backend.configuration().backend_name)
quil_to_native_quil_spy = mocker.spy(qc.compiler, "quil_to_native_quil")

expected_qasm = "\n".join(
[
"OPENQASM 2.0;",
'include "qelib1.inc";',
"qreg q[2];",
"creg ro[2];",
"h q[0];",
"#pragma PRESERVE_BLOCK;",
"#pragma END_PRESERVE_BLOCK;",
"h q[0];",
"measure q[0] -> ro[0];",
"measure q[1] -> ro[1];",
]
)

make_job(backend, circuit, qc)

program: Program = quil_to_native_quil_spy.call_args[0][0]
qasm = program.out(calibrations=False).rstrip()
assert qasm == expected_qasm


def test_init__circuit_with_barrier__qubit_subset(backend: RigettiQCSBackend, mocker: MockerFixture):
circuit = QuilCircuit(QuantumRegister(2, "q"), ClassicalRegister(2, "ro"))
circuit.h(0)
circuit.barrier(0)
circuit.h(0)
circuit.measure([0, 1], [0, 1])
qc = get_qc(backend.configuration().backend_name)
quil_to_native_quil_spy = mocker.spy(qc.compiler, "quil_to_native_quil")

expected_qasm = "\n".join(
[
"OPENQASM 2.0;",
'include "qelib1.inc";',
"qreg q[2];",
"creg ro[2];",
"h q[0];",
"h q[0];",
"measure q[0] -> ro[0];",
"measure q[1] -> ro[1];",
]
)

with pytest.warns(
UserWarning,
match=(
"barriers not applied to all circuit qubits will be omitted during execution on a RigettiQCSBackend -- "
"apply barrier to all circuit qubits to preserve barrier effect"
),
):
make_job(backend, circuit, qc)

program: Program = quil_to_native_quil_spy.call_args[0][0]
qasm = program.out(calibrations=False).rstrip()
assert qasm == expected_qasm


def test_result(job: RigettiQCSJob):
result = job.result()

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

assert job.status() == JobStatus.DONE

assert result.backend_name == "2q-qvm"
assert result.backend_name == "3q-qvm"
assert result.job_id == job.job_id()
assert result.success is True

Expand Down Expand Up @@ -176,16 +275,16 @@ def test_submit(job: RigettiQCSJob):

@pytest.fixture
def backend():
return RigettiQCSProvider().get_simulator(num_qubits=2)
return RigettiQCSProvider().get_simulator(num_qubits=3)


@pytest.fixture
def job(backend):
circuit = make_circuit(backend.configuration().num_qubits)
circuit = make_circuit(num_qubits=2)
return make_job(backend, circuit)


def make_circuit(num_qubits) -> QuilCircuit:
def make_circuit(*, num_qubits) -> QuilCircuit:
circuit = QuilCircuit(QuantumRegister(num_qubits, "q"), ClassicalRegister(num_qubits, "ro"))
circuit.h(0)
circuit.measure(range(num_qubits), range(num_qubits))
Expand Down

0 comments on commit 33ac2e7

Please sign in to comment.