Quantum programs

Module name: strawberryfields.program

This module implements the Program class which acts as a representation for quantum circuits. The Program object also acts as a context for defining the quantum circuit using the Python-embedded Blackbird syntax.

A typical use looks like

prog = sf.Program(num_subsystems)
with prog.context as q:
    Coherent(0.5)  | q[0]
    Vac            | q[1]
    Sgate(2)       | q[1]
    BSgate(1)      | q[0:2]
    Rgate(0.5)     | q[0]
    Dgate(0.5).H   | q[1]
    MeasureFock()  | q

eng = sf.LocalEngine(backend='fock', backend_options={'cutoff_dim': 5})
result = eng.run(prog)
assert prog.register[0].val == result.samples[0]

The Program objects keep track of the state of the quantum register they act on, using a dictionary of RegRef objects. The currently active register references can be accessed using the register() method.

Program methods

context Syntactic sugar for defining a Program using the with statement.
register Return symbolic references to all the currently valid register subsystems.
num_subsystems Return the current number of valid register subsystems.
__len__() Program length.
can_follow(prev) Check whether this program can follow the given program.
append(op, reg) Append a command to the program.
lock() Finalize the program.
compile(target, **kwargs) Compile the program targeting the given circuit template.
optimize() Simplify and optimize the program.
print([print_fn]) Print the program contents using Blackbird syntax.
draw_circuit([tex_dir, write_to_file]) Draw the circuit using the Qcircuit \(\LaTeX\) package.

The following are internal Program methods. In most cases the user should not call these directly.

__enter__() Enter the context for this program.
__exit__(ex_type, ex_value, ex_tb) Exit the quantum circuit context.
_clear_regrefs() Clear any measurement values stored in the RegRefs.
_add_subsystems(n) Create new subsystem references, add them to the reg_ref dictionary.
_delete_subsystems(refs) Delete existing subsystem references.
_index_to_regref(ind) Try to find a RegRef corresponding to a given subsystem index.
_test_regrefs(reg) Make sure reg is a valid selection of subsystems, convert them to RegRefs.
_linked_copy() Create a copy of the Program, linked to the original.

Module name: strawberryfields.program_utils

Helper classes

Command(op, reg) Represents a quantum operation applied on specific subsystems of the register.
RegRef(ind) Quantum register reference.
RegRefTransform(refs[, func, func_str]) Represents a scalar function of one or more register references.

Utility functions

list_to_grid(ls) Transforms a list of Commands to a grid representation.
grid_to_DAG(grid) Transforms a grid of Commands to a DAG representation.
list_to_DAG(ls) Transforms a list of Commands to a DAG representation.
DAG_to_list(dag) Transforms a Command DAG to a list representation.
group_operations(seq, predicate) Group a set of Operations in a circuit together (if possible).
optimize_circuit(seq) Try to simplify and optimize a quantum circuit.

Exceptions

MergeFailure Exception raised by strawberryfields.ops.Operation.merge() when an attempted merge fails.
CircuitError Exception raised by Program when it encounters an illegal operation in the quantum circuit.
RegRefError Exception raised by Program when it encounters an invalid register reference.

Quantum circuit representation

The Command instances in the circuit form a strict partially ordered set in the sense that the order in which the operations have to be executed is usually not completely fixed. For example, operations acting on different subsystems always commute with each other. We denote \(a < b\) if \(a\) has to be executed before \(b\). Each strict partial order corresponds to a directed acyclic graph (DAG), and the transitive closure of any DAG is a strict partial order. Three different (but equivalent) representations of the circuit are used.

  • Initially, the circuit is represented as a Command queue (list), listing the Commands in the temporal order they are applied.
  • The second representation, grid, essentially mimics a quantum circuit diagram. It is a mapping from subsystem indices to lists of Commands touching that subsystem, where each list is temporally ordered.
  • Finally, the quantum circuit can be represented using a DAG by making each Command a node, and drawing an edge from each Command to all its immediate followers along each wire it touches. It can be converted back into a command queue by popping a maximal element until the graph is empty, that is, consuming it in a topological order. Note that a topological order is not always unique, there may be several equivalent topological orders.

The three representations can be converted to each other using the functions list_to_grid(), grid_to_DAG() and DAG_to_list().

Code details

class strawberryfields.program.Program(num_subsystems, name=None)[source]

Represents a quantum circuit.

A quantum circuit is a set of quantum operations applied in a specific order to a set of subsystems (represented by wires in the circuit diagram) of the quantum register. The Program class represents a quantum circuit in general as a directed acyclic graph (DAG) whose nodes are Command instances, and each (directed) edge in the graph corresponds to a specific wire along which the two associated Commands are connected.

Program instances also act as context managers (and the context itself) for inputting quantum circuits using a with block and Operation instances. The contexts may not be nested.

The quantum circuit is inputted by using the __or__() methods of the quantum operations, which call the append() method of the Program. append() checks that the register references are valid and then adds a new Command instance to the Program.

The New and Del operations modify the quantum register itself by adding and deleting subsystems. The Program keeps track of these changes (using RegRef instances that represent the register) as they are appended to it in order to be able to report register reference errors as soon as they happen.

Program p2 can be run after Program p1 if the RegRef state at the end of p1 matches the RegRef state at the start of p2. This can be enforced by constructing p2 as an explicit successor of p1, in which case the regrefs are copied over. When a Program is run or it obtains a successor, it is locked and no more Commands can be appended to it.

Parameters:
  • num_subsystems (int, Program) – Initial number of subsystems in the quantum register. Alternatively, another Program instance from which to inherit the register state.
  • name (str) – program name (optional)
name = None

program name

Type:str
circuit = None

Commands constituting the quantum circuit in temporal order

Type:list[Command]
locked = None

if True, no more Commands can be appended to the Program

Type:bool
target = None

for compiled Programs, the short name of the target CircuitSpecs template, otherwise None

Type:str, None
source = None

for compiled Programs, this is the original, otherwise None

Type:Program, None
run_options = None

dictionary of default run options, to be passed to the engine upon execution of the program. Note that if the run_options dictionary is passed directly to run(), it takes precedence over the run options specified here.

Type:dict[str, Any]
init_num_subsystems = None

initial number of subsystems

Type:int
reg_refs = None

mapping from subsystem indices to corresponding RegRef objects

Type:dict[int, RegRef]
unused_indices = None

created subsystem indices that have not been used (operated on) yet

Type:set[int]
init_reg_refs = None

like reg_refs

Type:dict[int, RegRef]
init_unused_indices = None

like unused_indices

Type:set[int]
__len__()[source]

Program length.

Returns:number of Commands in the program
Return type:int
print(print_fn=<built-in function print>)[source]

Print the program contents using Blackbird syntax.

Parameters:print_fn (function) – optional custom function to use for string printing
context

Syntactic sugar for defining a Program using the with statement.

The Program object itself acts as the context manager.

register

Return symbolic references to all the currently valid register subsystems.

Returns:valid subsystem references
Return type:tuple[RegRef]
num_subsystems

Return the current number of valid register subsystems.

Returns:number of currently valid register subsystems
Return type:int
_clear_regrefs()[source]

Clear any measurement values stored in the RegRefs.

Called by Engine when resetting the backend.

_add_subsystems(n)[source]

Create new subsystem references, add them to the reg_ref dictionary.

To avoid discrepancies with the backend this method must not be called directly, but rather indirectly by using New() in a Program context.

Note

This is the only place where RegRef instances are constructed.

Parameters:n (int) – number of subsystems to add
Returns:tuple of the newly added subsystem references
Return type:tuple[RegRef]
_delete_subsystems(refs)[source]

Delete existing subsystem references.

To avoid discrepancies with the backend this method must not be called directly, but rather indirectly by using _Delete instances in the Program context.

Parameters:refs (Sequence[RegRef]) – subsystems to delete
lock()[source]

Finalize the program.

When a Program is locked, no more Commands can be appended to it. The locking happens when the program is run, compiled, or a successor Program is constructed, in order to ensure that the RegRef state of the Program does not change anymore.

can_follow(prev)[source]

Check whether this program can follow the given program.

This requires that the final RegRef state of the first program matches the initial RegRef state of the second program, i.e., they have the same number number of RegRefs, all with identical indices and activity states.

Parameters:prev (Program) – preceding program fragment
Returns:True if the Program can follow prev
Return type:bool
_index_to_regref(ind)[source]

Try to find a RegRef corresponding to a given subsystem index.

Parameters:ind (int) – subsystem index
Returns:corresponding register reference
Return type:RegRef
Raises:RegRefError – if the subsystem cannot be found, or is invalid
_test_regrefs(reg)[source]

Make sure reg is a valid selection of subsystems, convert them to RegRefs.

A register reference is valid if it is properly recorded in self.reg_refs and has not been deleted. The selection is valid if it contains only valid RegRefs and no subsystem is repeated.

Parameters:reg (Iterable[int, RegRef]) – subsystem references
Returns:converted subsystem references
Return type:list[RegRef]
Raises:RegRefError – if an invalid subsystem reference is found
append(op, reg)[source]

Append a command to the program.

Parameters:
  • op (Operation) – quantum operation
  • reg (list[int, RegRef]) – register subsystem(s) to apply it to
Returns:

subsystem list as RegRefs

Return type:

list[RegRef]

_linked_copy()[source]

Create a copy of the Program, linked to the original.

Both the original and the copy are locked, since they share their RegRefs.

Returns:a copy of the Program
Return type:Program
compile(target, **kwargs)[source]

Compile the program targeting the given circuit template.

Validates the program against the given target, making sure all the Operations used are accepted by the target template. Additionally, depending on the target, the compilation may modify the quantum circuit into an equivalent circuit, e.g., by decomposing certain gates into sequences of simpler gates, or optimizing the gate ordering using commutation rules.

The returned compiled Program shares its RegRefs with the original, which makes it easier to access the measurement results, but also necessitates the locking of both the compiled program and the original to make sure the RegRef state remains consistent.

Parameters:

target (str, CircuitSpecs) – short name of the target circuit specification, or the specification object itself

Keyword Arguments:
 
  • optimize (bool) – If True, try to optimize the program by merging and canceling gates. The default is False.
  • warn_connected (bool) – If True, the user is warned if the quantum circuit is not weakly connected. The default is True.
Returns:

compiled program

Return type:

Program

optimize()[source]

Simplify and optimize the program.

The simplifications are based on the algebraic properties of the gates, e.g., combining two consecutive gates of the same gate family.

Returns a copy of the program, sharing RegRefs with the original.

See optimize_circuit().

Returns:optimized copy of the program
Return type:Program
draw_circuit(tex_dir='./circuit_tex', write_to_file=True)[source]

Draw the circuit using the Qcircuit \(\LaTeX\) package.

This will generate the LaTeX code required to draw the quantum circuit diagram corresponding to the Program.

Parameters:
  • tex_dir (str) – relative directory for latex document output
  • write_to_file (bool) – if False, no output file is created
Returns:

filename of the written tex document and the written tex content

Return type:

list[str]

Quantum program utilities. Module docs in program.py.

strawberryfields.program_utils.Program_current_context = None

Context for inputting a Program. Used to be a class attribute of Program, placed here to avoid cyclic imports.

strawberryfields.program_utils._convert(func)[source]

Decorator for converting user defined functions to a RegRefTransform.

This allows classical processing of measured qumodes values.

Example usage:

@convert
def F(x):
    # some classical processing of x
    return f(x)

with prog.context as q:
    MeasureX       | q[0]
    Dgate(F(q[0])) | q[1]
Parameters:func (function) – function to be converted to a RegRefTransform.
exception strawberryfields.program_utils.RegRefError[source]

Exception raised by Program when it encounters an invalid register reference.

E.g., trying to apply a gate to a nonexistent or deleted subsystem.

exception strawberryfields.program_utils.CircuitError[source]

Exception raised by Program when it encounters an illegal operation in the quantum circuit.

E.g., trying to use a measurement result before it is available.

exception strawberryfields.program_utils.MergeFailure[source]

Exception raised by strawberryfields.ops.Operation.merge() when an attempted merge fails.

E.g., trying to merge two gates of different families.

class strawberryfields.program_utils.Command(op, reg)[source]

Represents a quantum operation applied on specific subsystems of the register.

A Command instance is immutable once created, and can be shared between several Program instances.

Parameters:
  • op (Operation) – quantum operation to apply
  • reg (Sequence[RegRef]) – Subsystems to which the operation is applied. Note that the order matters here.
op = None

quantum operation to apply

Type:Operation
reg = None

subsystems to which the operation is applied

Type:Sequence[RegRef]
get_dependencies()[source]

Subsystems the command depends on.

Combination of self.reg and self.op.extra_deps.

Note

extra_deps are used to ensure that the measurement happens before the result is used, but this is a bit too strict: two gates depending on the same measurement result but otherwise acting on different subsystems should commute.

Returns:set of subsystems the command depends on
Return type:set[RegRef]
class strawberryfields.program_utils.RegRef(ind)[source]

Quantum register reference.

The objects of this class refer to a specific subsystem (mode) of a quantum register.

Within the scope of each Program instance, only one RegRef instance should exist per subsystem. Program keeps the authoritative mapping of subsystem indices to RegRef instances. Subsystem measurement results are stored in the “official” RegRef object. If other RegRef objects referring to the same subsystem exist, they will not be updated. Once a RegRef is assigned a subsystem index it will never change, not even if the subsystem is deleted.

The RegRefs are constructed in Program._add_subsystems().

Parameters:ind (int) – index of the register subsystem referred to
ind = None

subsystem index

Type:int
val = None

Measurement result. None if the subsystem has not been measured yet.

Type:float, complex
active = None

True at construction, False after the subsystem is deleted

Type:bool
class strawberryfields.program_utils.RegRefTransform(refs, func=None, func_str=None)[source]

Represents a scalar function of one or more register references.

A RegRefTransform instance, given as a parameter to a Operation constructor, represents a dependence of the Operation on classical information obtained by measuring one or more subsystems.

Used for deferred measurements, i.e., using a measurement’s value symbolically in defining a gate before the numeric value of that measurement is available.

Parameters:
  • r (Sequence[RegRef]) – register references that act as parameters for the function
  • func (None, function) – Scalar function that takes the values of the register references in r as parameters. None is equivalent to the identity transformation lambda x: x.
  • func_str (str) – an optional argument containing the string representation of the function. This is useful if a lambda function is passed to the RegRefTransform, which would otherwise show in the program queue as RegRefTransform(q[0], <lambda>).
regrefs = None

register references that act as parameters for the function

Type:list[RegRef]
func = None

the transformation itself, returns a scalar

Type:None, function
evaluate()[source]

Evaluates the numeric value of the function if all the measurement values are available.

Returns:function value
Return type:Number
strawberryfields.program_utils.list_to_grid(ls)[source]

Transforms a list of Commands to a grid representation.

The grid is a mapping from subsystem indices to lists of Command instances touching that subsystem, in temporal order. The same Command instance will appear in each list that corresponds to one of its subsystems.

Parameters:ls (Iterable[Command]) – quantum circuit
Returns:same circuit in grid form
Return type:dict[int, list[Command]]
strawberryfields.program_utils.grid_to_DAG(grid)[source]

Transforms a grid of Commands to a DAG representation.

In the DAG (directed acyclic graph) each node is a Command instance, and edges point from Commands to their immediate dependents/followers.

Parameters:grid (dict[int, list[Command]]) – quantum circuit
Returns:same circuit in DAG form
Return type:networkx.DiGraph[Command]
strawberryfields.program_utils.list_to_DAG(ls)[source]

Transforms a list of Commands to a DAG representation.

In the DAG (directed acyclic graph) each node is a Command instance, and edges point from Commands to their immediate dependents/followers.

Parameters:ls (Iterable[Command]) – quantum circuit
Returns:same circuit in DAG form
Return type:networkx.DiGraph[Command]
strawberryfields.program_utils.DAG_to_list(dag)[source]

Transforms a Command DAG to a list representation.

The list contains the Command instances in (one possible) topological order, i.e., dependants following the operations they depend on.

Parameters:dag (networkx.DiGraph[Command]) – quantum circuit
Returns:same circuit in list form
Return type:list[Command]
strawberryfields.program_utils.group_operations(seq, predicate)[source]

Group a set of Operations in a circuit together (if possible).

For the purposes of this method, we call a Operation instance marked iff predicate returns True on it.

This method converts the quantum circuit in seq into an equivalent circuit A+B+C, where the Command instances in sequences A and C do not contain any marked Operations. The sequence B contains all marked Operations in the circuit, and possibly additional unmarked instances that could not be moved into A or C using the available commutation rules. Any of the three returned sequences can be empty (but if B is empty then so is C).

Parameters:
  • seq (Sequence[Command]) – quantum circuit
  • predicate (Callable[[Operation], bool]) – Grouping predicate. Returns True for the Operations to be grouped together, False for the others.
Returns:

A, B, C such that A+B+C is equivalent to seq,

and A and C do not contain any marked Operation instances.

Return type:

Tuple[Sequence[Command]]

strawberryfields.program_utils.optimize_circuit(seq)[source]

Try to simplify and optimize a quantum circuit.

The purpose of the optimizer is to simplify the circuit to make it cheaper and faster to execute. Different backends may require different types of optimization, but in general the fewer operations a circuit has, the faster it should run. The optimizer thus should convert the circuit into a simpler equivalent circuit.

The optimizations are based on the abstract algebraic properties of the Operations constituting the circuit, e.g., combining two consecutive gates of the same gate family, and at no point should require a matrix representation of any kind. The optimization must also not change the state of the RegRefs in any way.

Currently the optimization is very simple. It

  • merges neighboring state preparations and gates belonging to the same family and acting on the same sequence of subsystems
  • cancels neighboring pairs of a gate and its inverse
Parameters:seq (Sequence[Command]) – quantum circuit to optimize
Returns:optimized circuit
Return type:List[Command]