Skip to content

Commit

Permalink
BREAKING CHANGE: Setting the offsets property on Declare will raise…
Browse files Browse the repository at this point in the history
… a `ValueError` if no shared_region is set.

* setup tests for Declare

* BREAKING CHANGE: Setting the offsets property on ``Declare`` will raise
if no shared_region is set.

* update tests

* add note

* trust truthy/falsy values
  • Loading branch information
MarquessV authored Mar 21, 2023
1 parent 98061b5 commit 36c56a6
Show file tree
Hide file tree
Showing 6 changed files with 236 additions and 38 deletions.
4 changes: 2 additions & 2 deletions pyquil/api/_compiler_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from dataclasses import dataclass
from typing import Iterator, List, Optional

import qcs_sdk
from qcs_sdk.compiler.quilc import get_version_info
import rpcq
from qcs_api_client.client import QCSClientConfiguration
from rpcq.messages import TargetDevice as TargetQuantumProcessor
Expand Down Expand Up @@ -181,7 +181,7 @@ def get_version(self) -> str:
Get version info for compiler server.
"""

return qcs_sdk.get_quilc_version()
return get_version_info()

def compile_to_native_quil(self, request: CompileToNativeQuilRequest) -> CompileToNativeQuilResponse:
"""
Expand Down
132 changes: 100 additions & 32 deletions pyquil/quilbase.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,18 +145,22 @@ def _convert_to_rs_instructions(instrs: Iterable[AbstractInstruction]) -> List[q


def _convert_to_py_instruction(instr: quil_rs.Instruction) -> AbstractInstruction:
if not isinstance(instr, quil_rs.Instruction):
raise ValueError(f"{type(instr)} is not a valid Instruction type")
if instr.is_calibration_definition():
return DefCalibration._from_rs_calibration(instr.to_calibration_definition())
if instr.is_gate():
return Gate._from_rs_gate(instr.to_gate())
if instr.is_measure_calibration_definition():
return DefMeasureCalibration._from_rs_measure_calibration_definition(instr.to_measure_calibration_definition())
if instr.is_measurement():
return Measurement._from_rs_measurement(instr.to_measurement())
if isinstance(instr, quil_rs.Instruction):
# TODOV4: Will have to handle unit variants since they don't have inner data
instr = instr.inner()
if isinstance(instr, quil_rs.Declaration):
return Declare._from_rs_declaration(instr)
if isinstance(instr, quil_rs.DefCalibration):
return DefCalibration._from_rs_calibration(instr)
if isinstance(instr, quil_rs.Gate):
return Gate._from_rs_gate(instr)
if isinstance(instr, quil_rs.MeasureCalibrationDefinition):
return DefMeasureCalibration._from_rs_measure_calibration_definition(instr)
if isinstance(instr, quil_rs.Measurement):
return Measurement._from_rs_measurement(instr)
if isinstance(instr, quil_rs.Instruction):
raise NotImplementedError(f"The {type(instr)} Instruction hasn't been mapped to an AbstractInstruction yet.")
raise ValueError(f"{type(instr)} is not a valid Instruction type")


def _convert_to_py_instructions(instrs: Iterable[quil_rs.Instruction]) -> List[AbstractInstruction]:
Expand Down Expand Up @@ -1110,7 +1114,7 @@ def __repr__(self) -> str:
return "<PRAGMA {}>".format(self.command)


class Declare(AbstractInstruction):
class Declare(quil_rs.Declaration, AbstractInstruction):
"""
A DECLARE directive.
Expand All @@ -1120,22 +1124,98 @@ class Declare(AbstractInstruction):
"""

def __init__(
self,
@staticmethod
def __new__(
cls,
name: str,
memory_type: str,
memory_size: int = 1,
shared_region: Optional[str] = None,
offsets: Optional[Iterable[Tuple[int, str]]] = None,
):
self.name = name
self.memory_type = memory_type
self.memory_size = memory_size
self.shared_region = shared_region
vector = quil_rs.Vector(Declare._memory_type_to_scalar_type(memory_type), memory_size)
sharing = None
if shared_region is not None:
sharing = quil_rs.Sharing(shared_region, Declare._to_rs_offsets(offsets))
return super().__new__(cls, name, vector, sharing)

@classmethod
def _from_rs_declaration(cls, declaration: quil_rs.Declaration) -> "Declare":
return super().__new__(cls, declaration.name, declaration.size, declaration.sharing)

@staticmethod
def _memory_type_to_scalar_type(memory_type: str) -> quil_rs.ScalarType:
memory_type = memory_type.upper()
if memory_type == "BIT":
return quil_rs.ScalarType.Bit
if memory_type == "INTEGER":
return quil_rs.ScalarType.Integer
if memory_type == "REAL":
return quil_rs.ScalarType.Real
if memory_type == "OCTET":
return quil_rs.ScalarType.Octet
raise ValueError(f"{memory_type} is not a valid scalar type.")

@staticmethod
def _to_rs_offsets(offsets: Optional[Iterable[Tuple[int, str]]]):
if offsets is None:
offsets = []
self.offsets = offsets
return []
return [
quil_rs.Offset(offset, Declare._memory_type_to_scalar_type(memory_type)) for offset, memory_type in offsets
]

@property
def memory_type(self) -> str:
return str(super().size.data_type)

@memory_type.setter
def memory_type(self, memory_type: str):
vector = super().size
vector.data_type = Declare._memory_type_to_scalar_type(memory_type)
quil_rs.Declaration.size.__set__(self, vector)

@property
def memory_size(self) -> int:
return super().size.length

@memory_size.setter
def memory_size(self, memory_size: int):
vector = super().size
vector.length = memory_size
quil_rs.Declaration.size.__set__(self, vector)

@property
def shared_region(self) -> Optional[str]:
sharing = super().sharing
if sharing is None:
return None
return sharing.name

@shared_region.setter
def shared_region(self, shared_region: Optional[str]):
sharing = super().sharing
if sharing is None:
if shared_region is None:
return
sharing = quil_rs.Sharing(shared_region, [])
else:
sharing.name = shared_region
quil_rs.Declaration.sharing.__set__(self, sharing)

@property
def offsets(self) -> List[Tuple[int, str]]:
sharing = super().sharing
if sharing is None:
return []
return [(offset.offset, str(offset.data_type)) for offset in sharing.offsets]

@offsets.setter
def offsets(self, offsets: Optional[List[Tuple[int, str]]]):
sharing = super().sharing
if sharing is None:
raise ValueError("DECLARE without a shared region cannot use offsets")
sharing.offsets = Declare._to_rs_offsets(offsets)
quil_rs.Declaration.sharing.__set__(self, sharing)

def asdict(self) -> Dict[str, Union[Iterable[Tuple[int, str]], Optional[str], int]]:
return {
Expand All @@ -1147,19 +1227,7 @@ def asdict(self) -> Dict[str, Union[Iterable[Tuple[int, str]], Optional[str], in
}

def out(self) -> str:
ret = "DECLARE {} {}[{}]".format(self.name, self.memory_type, self.memory_size)
if self.shared_region:
ret += " SHARING {}".format(self.shared_region)
for offset in self.offsets:
ret += " OFFSET {} {}".format(offset[0], offset[1])

return ret

def __str__(self) -> str:
return self.out()

def __repr__(self) -> str:
return "<DECLARE {}>".format(self.name)
return str(self)


class RawInstr(AbstractInstruction):
Expand Down
68 changes: 68 additions & 0 deletions test/unit/__snapshots__/test_quilbase.ambr
Original file line number Diff line number Diff line change
@@ -1,3 +1,71 @@
# name: TestDeclare.test_asdict[Defaults]
dict({
'memory_size': 1,
'memory_type': 'BIT',
'name': 'ro',
'offsets': list([
]),
'shared_region': None,
})
# ---
# name: TestDeclare.test_asdict[With-Offsets]
dict({
'memory_size': 5,
'memory_type': 'BIT',
'name': 'ro',
'offsets': list([
tuple(
2,
'OCTET',
),
]),
'shared_region': 'theta',
})
# ---
# name: TestDeclare.test_asdict[With-Shared]
dict({
'memory_size': 5,
'memory_type': 'INTEGER',
'name': 'ro',
'offsets': list([
]),
'shared_region': 'theta',
})
# ---
# name: TestDeclare.test_asdict[With-Size]
dict({
'memory_size': 5,
'memory_type': 'OCTET',
'name': 'ro',
'offsets': list([
]),
'shared_region': None,
})
# ---
# name: TestDeclare.test_out[Defaults]
'DECLARE ro BIT[1]'
# ---
# name: TestDeclare.test_out[With-Offsets]
'DECLARE ro BIT[5] SHARING theta OFFSET 2 OCTET'
# ---
# name: TestDeclare.test_out[With-Shared]
'DECLARE ro INTEGER[5] SHARING theta'
# ---
# name: TestDeclare.test_out[With-Size]
'DECLARE ro OCTET[5]'
# ---
# name: TestDeclare.test_str[Defaults]
'DECLARE ro BIT[1]'
# ---
# name: TestDeclare.test_str[With-Offsets]
'DECLARE ro BIT[5] SHARING theta OFFSET 2 OCTET'
# ---
# name: TestDeclare.test_str[With-Shared]
'DECLARE ro INTEGER[5] SHARING theta'
# ---
# name: TestDeclare.test_str[With-Size]
'DECLARE ro OCTET[5]'
# ---
# name: TestDefCalibration.test_out[No-Params]
'''
DEFCAL Calibrate 0:
Expand Down
4 changes: 2 additions & 2 deletions test/unit/test_quantum_computer.py
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,7 @@ def test_qc_error(client_configuration: QCSClientConfiguration):
get_qc("5q", as_qvm=False, client_configuration=client_configuration)


# TODO: Instruction API - Declare
# TODO: pickle compatibility
@pytest.mark.parametrize("param", [np.pi, [np.pi], np.array([np.pi])])
def test_run_with_parameters(client_configuration: QCSClientConfiguration, param):
quantum_processor = NxQuantumProcessor(nx.complete_graph(3))
Expand Down Expand Up @@ -508,7 +508,7 @@ def test_run_with_bad_parameters(client_configuration: QCSClientConfiguration, p
executable.write_memory(region_name="theta", value=param)


# TODO: Instruction API - Declare
# TODO: pickle compatibility
def test_reset(client_configuration: QCSClientConfiguration):
quantum_processor = NxQuantumProcessor(nx.complete_graph(3))
qc = QuantumComputer(
Expand Down
1 change: 0 additions & 1 deletion test/unit/test_quil.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,6 @@ def test_prog_init(snapshot):
assert p.out() == snapshot


# TODO: Instruction API - Declare
def test_classical_regs():
p = Program()
p.inst(
Expand Down
65 changes: 64 additions & 1 deletion test/unit/test_quilbase.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from math import pi
from typing import List, Optional
from typing import List, Optional, Iterable, Tuple

import pytest
from syrupy.assertion import SnapshotAssertion
Expand All @@ -8,6 +8,7 @@
from pyquil.quil import Program
from pyquil.quilbase import (
AbstractInstruction,
Declare,
DefCalibration,
DefFrame,
DefMeasureCalibration,
Expand Down Expand Up @@ -266,3 +267,65 @@ def test_center_frequency(self, def_frame: DefFrame, center_frequency: Optional[
assert def_frame.center_frequency == center_frequency
def_frame.center_frequency = 432.0
assert def_frame.center_frequency == 432.0


@pytest.mark.parametrize(
("name", "memory_type", "memory_size", "shared_region", "offsets"),
[
("ro", "BIT", 1, None, None),
("ro", "OCTET", 5, None, None),
("ro", "INTEGER", 5, "theta", None),
("ro", "BIT", 5, "theta", [(2, "OCTET")]),
],
ids=("Defaults", "With-Size", "With-Shared", "With-Offsets"),
)
class TestDeclare:
@pytest.fixture
def declare(
self,
name: str,
memory_type: str,
memory_size: int,
shared_region: Optional[str],
offsets: Optional[Iterable[Tuple[int, str]]],
) -> Declare:
return Declare(name, memory_type, memory_size, shared_region, offsets)

def test_out(self, declare: Declare, snapshot: SnapshotAssertion):
assert declare.out() == snapshot

def test_str(self, declare: Declare, snapshot: SnapshotAssertion):
assert str(declare) == snapshot

def test_asdict(self, declare: Declare, snapshot: SnapshotAssertion):
assert declare.asdict() == snapshot

def test_name(self, declare: Declare, name: str):
assert declare.name == name
declare.name = "new_name"
assert declare.name == "new_name"

def test_memory_type(self, declare: Declare, memory_type: Optional[str]):
assert declare.memory_type == memory_type
declare.memory_type = "REAL"
assert declare.memory_type == "REAL"

def test_memory_size(self, declare: Declare, memory_size: Optional[int]):
assert declare.memory_size == memory_size
declare.memory_size = 100
assert declare.memory_size == 100

def test_shared_region(self, declare: Declare, shared_region: Optional[str]):
assert declare.shared_region == shared_region
declare.shared_region = "new_shared"
assert declare.shared_region == "new_shared"

def test_offsets(self, declare: Declare, offsets: Optional[Iterable[Tuple[int, str]]]):
expected_offsets = offsets or []
assert declare.offsets == expected_offsets
if declare.shared_region is None:
with pytest.raises(ValueError):
declare.offsets = [(1, "BIT"), (2, "INTEGER")]
else:
declare.offsets = [(1, "BIT"), (2, "INTEGER")]
assert declare.offsets == [(1, "BIT"), (2, "INTEGER")]

0 comments on commit 36c56a6

Please sign in to comment.