Skip to content

Commit

Permalink
Updates to device specfication and program validation (#661)
Browse files Browse the repository at this point in the history
* update gate params and layout

* updates

* add to changelog

* remove unused import

* more changelog

* minor spelling

* Apply suggestions from code review

Co-authored-by: Mikhail Andrenkov <Mandrenkov@users.noreply.github.com>

* updates from code review

* add test

* add test

* update changelog

* Apply suggestions from code review

Co-authored-by: Mikhail Andrenkov <Mandrenkov@users.noreply.github.com>

* suggestions from code review

* fix passing empty params dict to validation

* fix tests

* add test

Co-authored-by: Mikhail Andrenkov <Mandrenkov@users.noreply.github.com>
  • Loading branch information
thisac and Mandrenkov committed Jan 14, 2022
1 parent 298601e commit d17e119
Show file tree
Hide file tree
Showing 5 changed files with 192 additions and 30 deletions.
15 changes: 13 additions & 2 deletions .github/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,27 @@

<h3>New features since last release</h3>

* `DeviceSpec.layout` and `DeviceSpec.gate_parameters` may now return `None`. This can happen
when a remote simulator device is used.
[(#661)](https://github.com/XanaduAI/strawberryfields/pull/661)

<h3>Breaking Changes</h3>

<h3>Bug fixes</h3>

* `program.compile` now raises an error if the device specification contains gate parameters but no
circuit layout. Without a layout, the gate parameters cannot be validated against the device
specification.
[(#661)](https://github.com/XanaduAI/strawberryfields/pull/661)

<h3>Documentation</h3>

<h3>Contributors</h3>

This release contains contributions from (in alphabetical order):

Theodor Isacsson

# Release 0.21.0 (current release)

<h3>New features since last release</h3>
Expand Down Expand Up @@ -492,7 +503,7 @@ Quesada, Antal Száva, Yuan Yao.
modes = 4
cutoff_dim = 6

# prepare an intial state with 4 photons in as many modes
# prepare an initial state with 4 photons in as many modes
initial_state = np.zeros([cutoff_dim] * modes, dtype=complex)
initial_state[1, 1, 1, 1] = 1

Expand Down Expand Up @@ -584,7 +595,7 @@ Quesada, Antal Száva, Yuan Yao.
* `measure_threshold` in the `gaussian` backend now supports displaced Gaussian states.
[(#615)](https://github.com/XanaduAI/strawberryfields/pull/615)

* Speed improvements are addded to ``gaussian_unitary`` compiler.
* Speed improvements are added to ``gaussian_unitary`` compiler.
[(#603)](https://github.com/XanaduAI/strawberryfields/pull/603)

* Adds native support in the Fock backend for the MZgate.
Expand Down
29 changes: 19 additions & 10 deletions strawberryfields/devicespec.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
This module contains a class that represents the specifications of
a device available via the API.
"""
from typing import Sequence, Mapping, Any
from typing import Sequence, Mapping, Any, Optional

import blackbird
from blackbird.error import BlackbirdSyntaxError
Expand Down Expand Up @@ -52,7 +52,7 @@ def target(self) -> str:
return self._spec["target"]

@property
def layout(self) -> str:
def layout(self) -> Optional[str]:
"""str: Returns a string containing the Blackbird circuit layout."""
return self._spec["layout"]

Expand All @@ -78,7 +78,7 @@ def default_compiler(self) -> str:
return "Xunitary"

@property
def gate_parameters(self) -> Mapping[str, Ranges]:
def gate_parameters(self) -> Optional[Mapping[str, Ranges]]:
"""dict[str, strawberryfields.compilers.Ranges]: A dictionary of gate parameters
and allowed ranges.
Expand All @@ -89,8 +89,10 @@ def gate_parameters(self) -> Mapping[str, Ranges]:
>>> spec.gate_parameters
{'squeezing_amplitude_0': x=0, x=1, 'phase_0': x=0, 0≤x≤6.283185307179586}
"""
gate_parameters = {}
if self._spec["gate_parameters"] is None:
return None

gate_parameters = {}
for gate_name, param_ranges in self._spec["gate_parameters"].items():
# convert gate parameter allowed ranges to Range objects
range_list = [[i] if not isinstance(i, Sequence) else i for i in param_ranges]
Expand All @@ -107,6 +109,9 @@ def validate_parameters(self, **parameters: complex) -> None:
Raises:
ValueError: if an invalid parameter is passed
"""
if self.gate_parameters is None:
return

# check that all provided parameters are valid
for p, v in parameters.items():
if p in self.gate_parameters and v not in self.gate_parameters[p]:
Expand Down Expand Up @@ -141,19 +146,23 @@ def create_program(self, **parameters: complex):
Returns:
strawberryfields.program.Program: program compiled to the device
"""
if not self.layout:
raise ValueError("Cannot create program. Specification is missing a circuit layout.")

try:
bb = blackbird.loads(self.layout)
except BlackbirdSyntaxError as e:
raise BlackbirdSyntaxError("Layout is not formatted correctly.") from e
self.validate_parameters(**parameters)

# determine parameter value if not provided
extra_params = set(self.gate_parameters) - set(parameters)
if self.gate_parameters:
# determine parameter value if not provided
extra_params = set(self.gate_parameters) - set(parameters)

for p in extra_params:
# Set parameter value as the first allowed
# value in the gate parameters dictionary.
parameters[p] = self.gate_parameters[p].ranges[0].x
for p in extra_params:
# Set parameter value as the first allowed
# value in the gate parameters dictionary.
parameters[p] = self.gate_parameters[p].ranges[0].x

# evaluate the blackbird template
bb = bb(**parameters)
Expand Down
9 changes: 7 additions & 2 deletions strawberryfields/program.py
Original file line number Diff line number Diff line change
Expand Up @@ -680,8 +680,13 @@ def _get_compiler(compiler_or_name):
backend_options = {k: kwargs[k] for k in kwargs if k not in ALLOWED_RUN_OPTIONS}
compiled.backend_options.update(backend_options)

# validate gate parameters
if device is not None and device.gate_parameters:
# if device spec has allowed gate parameters, validate the applied gate parameters
if device and device.gate_parameters:
if not device.layout:
raise ValueError(
"Gate parameters cannot be validated. Device specification is missing a "
"circuit layout."
)
bb_device = bb.loads(device.layout)
bb_compiled = sf.io.to_blackbird(compiled)

Expand Down
61 changes: 61 additions & 0 deletions tests/api/test_devicespec.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,24 @@ def test_create_program(self):
assert prog.circuit
assert [str(cmd) for cmd in prog.circuit] == circuit

def test_create_program_no_layout(self):
"""Test that the program creation raises an error if the device spec contains no layout"""

params = {"phase_0": 1.23}
device_dict_no_layout = {
"target": "abc",
"layout": None,
"modes": 2,
"compiler": ["Xcov"],
"gate_parameters": {
"squeezing_amplitude_0": [0, 1],
"phase_0": [0, [0, 6.3]],
"phase_1": [[0.5, 1.4]],
},
}
with pytest.raises(ValueError, match="missing a circuit layout"):
DeviceSpec(spec=device_dict_no_layout).create_program(**params)

@pytest.mark.parametrize(
"params", [{"phase_0": 7.5}, {"phase_1": 0.4}, {"squeezing_amplitude_0": 0.5}]
)
Expand Down Expand Up @@ -148,3 +166,46 @@ def test_invalid_spec(self):
ValueError, match=r"missing the following keys: \['gate_parameters', 'layout'\]"
):
DeviceSpec(spec=invalid_spec)

@pytest.mark.parametrize(
"params",
[
{"phase_0": 1.23},
{"phase_0": 0},
{"phase_0": 6.29999},
],
)
def test_valid_parameters(self, params):
"""Test that valid parameters pass the validate_parameters validation"""
DeviceSpec(spec=device_dict).validate_parameters(**params)

def test_invalid_parameter(self):
"""Test that invalid parameter names raise an error in validate_parameters"""
with pytest.raises(ValueError, match=r"not a valid parameter for this device"):
DeviceSpec(spec=device_dict).validate_parameters(phase_42=0)

def test_invalid_parameters_value(self):
"""Test that invalid parameter values raise an error in validate_parameters"""
with pytest.raises(ValueError, match=r"has invalid value"):
DeviceSpec(spec=device_dict).validate_parameters(phase_0=123)

@pytest.mark.parametrize(
"params",
[
{"phase_0": 1.23},
{"phase_0": 0},
{"phase_0": 6.29999},
{"phase_0": 123},
{"phase_42": 123},
],
)
def test_gate_parameters_none(self, params):
"""Test that any parameters a valid when gate_parameters is None"""
device_dict = {
"target": "abc",
"layout": mock_layout,
"modes": 2,
"compiler": ["Xcov"],
"gate_parameters": None,
}
DeviceSpec(spec=device_dict).validate_parameters(**params)

0 comments on commit d17e119

Please sign in to comment.