Source code for strawberryfields.io.blackbird_io
# Copyright 2019-2021 Xanadu Quantum Technologies Inc.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
This module contains functions for loading and saving Strawberry Fields
:class:`~.Program` objects from/to Blackbird scripts and Strawberry Fields
code.
"""
# pylint: disable=protected-access,too-many-nested-blocks
import numpy as np
import blackbird
import strawberryfields.parameters as sfpar
from strawberryfields.program import Program
from strawberryfields.tdm import TDMProgram, is_ptype
from strawberryfields import ops
def from_blackbird(bb: blackbird.BlackbirdProgram) -> Program:
"""Convert a Blackbird program to a Strawberry Fields program.
Args:
bb (blackbird.BlackbirdProgram): the input Blackbird program object
Returns:
Program: corresponding Strawberry Fields program
Raises:
NameError: if an applied quantum operation is not defined in Strawberry Fields
"""
# create a SF program
prog = Program(max(bb.modes) + 1, name=bb.name)
# append the quantum operations
with prog.context as q:
for op in bb.operations:
# check if operation name is in the list of
# defined StrawberryFields operations.
# This is used by checking against the ops.py __all__
# module attribute, which contains the names
# of all defined quantum operations
if op["op"] in ops.__all__:
# get the quantum operation from the sf.ops module
gate = getattr(ops, op["op"])
else:
raise NameError("Quantum operation {} not defined!".format(op["op"]))
# create the list of regrefs
regrefs = [q[i] for i in op["modes"]]
if "args" in op and "kwargs" in op:
# the gate has arguments
args = op["args"]
kwargs = op["kwargs"]
# Convert symbolic expressions in args/kwargs containing measured and free parameters to
# symbolic expressions containing the corresponding MeasuredParameter and FreeParameter instances.
args = sfpar.par_convert(args, prog)
vals = sfpar.par_convert(kwargs.values(), prog)
kwargs = dict(zip(kwargs.keys(), vals))
gate(*args, **kwargs) | regrefs # pylint:disable=expression-not-assigned
else:
# the gate has no arguments
gate | regrefs # pylint:disable=expression-not-assigned,pointless-statement
prog._target = bb.target["name"]
if "shots" in bb.target["options"]:
prog.run_options["shots"] = bb.target["options"]["shots"]
if "cutoff_dim" in bb.target["options"]:
prog.backend_options["cutoff_dim"] = bb.target["options"]["cutoff_dim"]
return prog
# pylint:disable=too-many-branches
def from_blackbird_to_tdm(bb: blackbird.BlackbirdProgram) -> TDMProgram:
"""Convert a ``BlackbirdProgram`` to a ``TDMProgram``.
Args:
bb (blackbird.BlackbirdProgram): the input Blackbird program object
Returns:
TDMProgram: corresponding ``TDMProgram``
Raises:
NameError: if an applied quantum operation is not defined in Strawberry Fields
"""
prog = TDMProgram(max(bb.modes) + 1, name=bb.name)
def is_free_param(param):
return isinstance(param, str) and is_ptype(param)
# retrieve all the free parameters in the Blackbird program (e.g. "p0", "p1"
# etc.) and add their corresponding values to args
args = []
for k in bb._var.keys():
if is_free_param(k):
v = bb._var[k].flatten()
args.append(v)
# append the quantum operations
with prog.context(*args) as (p, q):
for op in bb.operations:
# check if operation name is in the list of
# defined StrawberryFields operations.
# This is used by checking against the ops.py __all__
# module attribute, which contains the names
# of all defined quantum operations
if op["op"] in ops.__all__:
# get the quantum operation from the sf.ops module
gate = getattr(ops, op["op"])
else:
raise NameError("Quantum operation {} not defined!".format(op["op"]))
# create the list of regrefs
regrefs = [q[i] for i in op["modes"]]
if "args" in op:
# the gate has arguments
args = op["args"]
kwargs = op["kwargs"]
for i, p in enumerate(args):
if is_free_param(p):
args[i] = sfpar.FreeParameter(p)
for k, v in kwargs.items():
if is_free_param(v):
kwargs[k] = sfpar.FreeParameter(v)
# Convert symbolic expressions in args/kwargs containing measured and free parameters to
# symbolic expressions containing the corresponding MeasuredParameter and FreeParameter instances.
args = sfpar.par_convert(args, prog)
vals = sfpar.par_convert(kwargs.values(), prog)
kwargs = dict(zip(kwargs.keys(), vals))
gate(*args, **kwargs) | regrefs # pylint:disable=expression-not-assigned
else:
# the gate has no arguments
gate | regrefs # pylint:disable=expression-not-assigned,pointless-statement
prog._target = bb.target["name"]
if "shots" in bb.target["options"]:
prog.run_options["shots"] = bb.target["options"]["shots"]
if "cutoff_dim" in bb.target["options"]:
prog.backend_options["cutoff_dim"] = bb.target["options"]["cutoff_dim"]
return prog
[docs]def to_blackbird(prog: Program, version: str = "1.0") -> blackbird.BlackbirdProgram:
"""Convert a Strawberry Fields Program to a Blackbird Program.
Args:
prog (Program): the Strawberry Fields program
version (str): Blackbird script version number
Returns:
blackbird.BlackbirdProgram:
"""
bb = blackbird.BlackbirdProgram(name=prog.name, version=version)
bb._modes = set(prog.reg_refs.keys())
isMeasuredParameter = lambda x: isinstance(x, sfpar.MeasuredParameter)
# not sure if this makes sense: the program has *already been* compiled using this target
if prog.target is not None:
# set the target
bb._target["name"] = prog.target
# set the run options
if prog.run_options:
bb._target["options"].update(prog.run_options)
if prog.backend_options:
bb._target["options"].update(prog.backend_options)
# fill in the quantum circuit
for cmd in prog.circuit:
op = {"kwargs": {}, "args": []}
op["op"] = cmd.op.__class__.__name__
op["modes"] = [i.ind for i in cmd.reg]
if "Measure" in op["op"]:
# special case to take into account 'select' keyword argument
if cmd.op.select is not None:
op["kwargs"]["select"] = cmd.op.select
if cmd.op.p:
# argument is quadrature phase
op["args"] = cmd.op.p
if op["op"] == "MeasureFock":
# special case to take into account 'dark_counts' keyword argument
if cmd.op.dark_counts is not None:
op["kwargs"]["dark_counts"] = cmd.op.dark_counts
else:
for a in cmd.op.p:
if sfpar.par_is_symbolic(a):
# SymPy object, convert to string
if any(map(isMeasuredParameter, a.free_symbols)):
# check if there are any measured parameters in `a`
a = blackbird.RegRefTransform(a)
else:
a = str(a)
op["args"].append(a)
# If program is a TDMProgram then add the looped-over arrays to the
# blackbird program. `prog.loop_vars` are symbolic parameters (e.g.
# `{p0}`), which should be replaced with `p.name` (e.g. `p0`) inside the
# Blackbird operation (keyword) arguments.
if isinstance(prog, TDMProgram):
for p in prog.loop_vars:
for i, ar in enumerate(op["args"]):
if str(p) == str(ar):
op["args"][i] = p.name
for k, v in op["kwargs"].items():
if str(p) == str(v):
op["kwargs"][k] = p.name
bb._operations.append(op)
# add the specific "tdm" metadata to the Blackbird program
if isinstance(prog, TDMProgram):
bb._type["name"] = "tdm"
bb._type["options"].update(
{
"temporal_modes": prog.timebins,
}
)
bb._var.update(
{f"{p.name}": np.array([prog.tdm_params[i]]) for i, p in enumerate(prog.loop_vars)}
)
return bb
_modules/strawberryfields/io/blackbird_io
Download Python script
Download Notebook
View on GitHub