Skip to content

Commit 6f5b4b2

Browse files
jselig-rigettipavoljuhas
authored andcommitted
Update cirq-rigetti to use pyquil v4 (#6281)
- Add support for Kraus operators, POVMs and parametric defgates - Update quil->cirq conversion Fixes #6500 Partially implements #6464
1 parent e228340 commit 6f5b4b2

13 files changed

+815
-363
lines changed

cirq-rigetti/cirq_rigetti/_qcs_api_client_decorator.py

-43
This file was deleted.

cirq-rigetti/cirq_rigetti/aspen_device.py

+13-18
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,16 @@
1111
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
14-
from typing import List, cast, Optional, Union, Dict, Any
14+
from typing import List, Optional, Union, Dict, Any
1515
import functools
1616
from math import sqrt
17-
import httpx
17+
import json
1818
import numpy as np
1919
import networkx as nx
2020
import cirq
2121
from pyquil.quantum_processor import QCSQuantumProcessor
22-
from qcs_api_client.models import InstructionSetArchitecture
23-
from qcs_api_client.operations.sync import get_instruction_set_architecture
24-
from cirq_rigetti._qcs_api_client_decorator import _provide_default_client
22+
from qcs_sdk.client import QCSClient
23+
from qcs_sdk.qpu.isa import get_instruction_set_architecture, InstructionSetArchitecture, Family
2524

2625

2726
class UnsupportedQubit(ValueError):
@@ -50,6 +49,8 @@ class UnsupportedRigettiQCSQuantumProcessor(ValueError):
5049
class RigettiQCSAspenDevice(cirq.devices.Device):
5150
"""A cirq.Qid supporting Rigetti QCS Aspen device topology."""
5251

52+
isa: InstructionSetArchitecture
53+
5354
def __init__(self, isa: Union[InstructionSetArchitecture, Dict[str, Any]]) -> None:
5455
"""Initializes a RigettiQCSAspenDevice with its Rigetti QCS `InstructionSetArchitecture`.
5556
@@ -63,9 +64,9 @@ def __init__(self, isa: Union[InstructionSetArchitecture, Dict[str, Any]]) -> No
6364
if isinstance(isa, InstructionSetArchitecture):
6465
self.isa = isa
6566
else:
66-
self.isa = InstructionSetArchitecture.from_dict(isa)
67+
self.isa = InstructionSetArchitecture.from_raw(json.dumps(isa))
6768

68-
if self.isa.architecture.family.lower() != 'aspen':
69+
if self.isa.architecture.family != Family.Aspen:
6970
raise UnsupportedRigettiQCSQuantumProcessor(
7071
'this integration currently only supports Aspen devices, '
7172
f'but client provided a {self.isa.architecture.family} device'
@@ -224,23 +225,22 @@ def __repr__(self):
224225
return f'cirq_rigetti.RigettiQCSAspenDevice(isa={self.isa!r})'
225226

226227
def _json_dict_(self):
227-
return {'isa': self.isa.to_dict()}
228+
return {'isa': json.loads(self.isa.json())}
228229

229230
@classmethod
230231
def _from_json_dict_(cls, isa, **kwargs):
231-
return cls(isa=InstructionSetArchitecture.from_dict(isa))
232+
return cls(isa=InstructionSetArchitecture.from_raw(json.dumps(isa)))
232233

233234

234-
@_provide_default_client # pragma: no cover
235235
def get_rigetti_qcs_aspen_device(
236-
quantum_processor_id: str, client: Optional[httpx.Client]
236+
quantum_processor_id: str, client: Optional[QCSClient] = None
237237
) -> RigettiQCSAspenDevice:
238238
"""Retrieves a `qcs_api_client.models.InstructionSetArchitecture` from the Rigetti
239239
QCS API and uses it to initialize a RigettiQCSAspenDevice.
240240
241241
Args:
242242
quantum_processor_id: The identifier of the Rigetti QCS quantum processor.
243-
client: Optional; A `httpx.Client` initialized with Rigetti QCS credentials
243+
client: Optional; A `QCSClient` initialized with Rigetti QCS credentials
244244
and configuration. If not provided, `qcs_api_client` will initialize a
245245
configured client based on configured values in the current user's
246246
`~/.qcs` directory or default values.
@@ -250,12 +250,7 @@ def get_rigetti_qcs_aspen_device(
250250
set and architecture.
251251
252252
"""
253-
isa = cast(
254-
InstructionSetArchitecture,
255-
get_instruction_set_architecture(
256-
client=client, quantum_processor_id=quantum_processor_id
257-
).parsed,
258-
)
253+
isa = get_instruction_set_architecture(client=client, quantum_processor_id=quantum_processor_id)
259254
return RigettiQCSAspenDevice(isa=isa)
260255

261256

cirq-rigetti/cirq_rigetti/aspen_device_test.py

+17-19
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
from unittest.mock import patch, PropertyMock
44
from math import sqrt
55
import pathlib
6-
import json
76
import pytest
87
import cirq
98
from cirq_rigetti import (
@@ -12,9 +11,8 @@
1211
RigettiQCSAspenDevice,
1312
UnsupportedQubit,
1413
UnsupportedRigettiQCSOperation,
15-
UnsupportedRigettiQCSQuantumProcessor,
1614
)
17-
from qcs_api_client.models import InstructionSetArchitecture, Node
15+
from qcs_sdk.qpu.isa import InstructionSetArchitecture, Family
1816
import numpy as np
1917

2018
dir_path = pathlib.Path(os.path.dirname(os.path.realpath(__file__)))
@@ -24,7 +22,7 @@
2422
@pytest.fixture
2523
def qcs_aspen8_isa() -> InstructionSetArchitecture:
2624
with open(fixture_path / 'QCS-Aspen-8-ISA.json', 'r') as f:
27-
return InstructionSetArchitecture.from_dict(json.load(f))
25+
return InstructionSetArchitecture.from_raw(f.read())
2826

2927

3028
def test_octagonal_qubit_index():
@@ -204,17 +202,6 @@ def test_rigetti_qcs_aspen_device_invalid_qubit(
204202
device.validate_operation(cirq.I(qubit))
205203

206204

207-
def test_rigetti_qcs_aspen_device_non_existent_qubit(qcs_aspen8_isa: InstructionSetArchitecture):
208-
"""test RigettiQCSAspenDevice throws error when qubit does not exist on device"""
209-
# test device may only be initialized with Aspen ISA.
210-
device_with_limited_nodes = RigettiQCSAspenDevice(
211-
isa=InstructionSetArchitecture.from_dict(qcs_aspen8_isa.to_dict())
212-
)
213-
device_with_limited_nodes.isa.architecture.nodes = [Node(node_id=10)]
214-
with pytest.raises(UnsupportedQubit):
215-
device_with_limited_nodes.validate_qubit(cirq.GridQubit(0, 0))
216-
217-
218205
@pytest.mark.parametrize(
219206
'operation',
220207
[
@@ -265,7 +252,18 @@ def test_rigetti_qcs_aspen_device_repr(qcs_aspen8_isa: InstructionSetArchitectur
265252

266253
def test_rigetti_qcs_aspen_device_family_validation(qcs_aspen8_isa: InstructionSetArchitecture):
267254
"""test RigettiQCSAspenDevice validates architecture family on initialization"""
268-
non_aspen_isa = InstructionSetArchitecture.from_dict(qcs_aspen8_isa.to_dict())
269-
non_aspen_isa.architecture.family = "not-aspen" # type: ignore
270-
with pytest.raises(UnsupportedRigettiQCSQuantumProcessor):
271-
RigettiQCSAspenDevice(isa=non_aspen_isa)
255+
non_aspen_isa = InstructionSetArchitecture.from_raw(qcs_aspen8_isa.json())
256+
non_aspen_isa.architecture.family = Family.NONE
257+
258+
assert (
259+
non_aspen_isa.architecture.family == Family.Aspen
260+
), 'ISA family is read-only and should still be Aspen'
261+
262+
263+
def test_get_rigetti_qcs_aspen_device(qcs_aspen8_isa: InstructionSetArchitecture):
264+
with patch('cirq_rigetti.aspen_device.get_instruction_set_architecture') as mock:
265+
mock.return_value = qcs_aspen8_isa
266+
267+
from cirq_rigetti.aspen_device import get_rigetti_qcs_aspen_device
268+
269+
assert get_rigetti_qcs_aspen_device('Aspen-8') == RigettiQCSAspenDevice(isa=qcs_aspen8_isa)

cirq-rigetti/cirq_rigetti/circuit_sweep_executors.py

+11-12
Original file line numberDiff line numberDiff line change
@@ -57,21 +57,22 @@ def _execute_and_read_result(
5757
Raises:
5858
ValueError: measurement_id_map references an undefined pyQuil readout region.
5959
"""
60-
if memory_map is None:
61-
memory_map = {}
6260

63-
for region_name, values in memory_map.items():
64-
if isinstance(region_name, str):
65-
executable.write_memory(region_name=region_name, value=values)
66-
else:
67-
raise ValueError(f'Symbols not valid for region name {region_name}')
68-
qam_execution_result = quantum_computer.qam.run(executable)
61+
# convert all atomic memory values into 1-length lists
62+
if memory_map is not None:
63+
for region_name, value in memory_map.items():
64+
if not isinstance(region_name, str):
65+
raise ValueError(f'Symbols not valid for region name {region_name}')
66+
value = [value] if not isinstance(value, Sequence) else value
67+
memory_map[region_name] = value
68+
69+
qam_execution_result = quantum_computer.qam.run(executable, memory_map) # type: ignore
6970

7071
measurements = {}
7172
# For every key, value in QuilOutput#measurement_id_map, use the value to read
7273
# Rigetti QCS results and assign to measurements by key.
7374
for cirq_memory_key, pyquil_region in measurement_id_map.items():
74-
readout = qam_execution_result.readout_data.get(pyquil_region)
75+
readout = qam_execution_result.get_register_map().get(pyquil_region)
7576
if readout is None:
7677
raise ValueError(f'readout data does not have values for region "{pyquil_region}"')
7778
measurements[cirq_memory_key] = readout
@@ -122,9 +123,7 @@ def _prepend_real_declarations(
122123
param_dict = _get_param_dict(resolver)
123124
for key in param_dict.keys():
124125
declaration = Declare(str(key), "REAL")
125-
program._instructions.insert(0, declaration)
126-
program._synthesized_instructions = None
127-
program.declarations[declaration.name] = declaration
126+
program = Program(declaration) + program
128127
logger.debug(f"prepended declaration {declaration}")
129128
return program
130129

cirq-rigetti/cirq_rigetti/circuit_transformers_test.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from unittest.mock import create_autospec
44
import cirq
55
import numpy as np
6+
from pyquil import Program
67
from pyquil.gates import MEASURE, RX, DECLARE, H, CNOT, I
78
from pyquil.quilbase import Pragma, Reset
89
from cirq_rigetti import circuit_transformers as transformers
@@ -63,15 +64,15 @@ def test_transform_with_post_transformation_hooks(
6364
bell_circuit, qubits = bell_circuit_with_qids
6465

6566
def reset_hook(program, measurement_id_map):
66-
program._instructions.insert(0, Reset())
67+
program = Program(Reset()) + program
6768
return program, measurement_id_map
6869

6970
reset_hook_spec = create_autospec(reset_hook, side_effect=reset_hook)
7071

7172
pragma = Pragma('INTIAL_REWIRING', freeform_string='GREEDY')
7273

7374
def rewire_hook(program, measurement_id_map):
74-
program._instructions.insert(0, pragma)
75+
program = Program(pragma) + program
7576
return program, measurement_id_map
7677

7778
rewire_hook_spec = create_autospec(rewire_hook, side_effect=rewire_hook)

0 commit comments

Comments
 (0)