Compile diagrams to abstract syntax trees (AST) and python functions. For example:

>>> import polycirc.ir as ir
>>> d = ir.add(1) >> ir.negate(1) # diagram representing -(x + y)
>>> a = diagram_to_ast(d, 'fn') # convert the diagram to an AST
>>> fn = a.to_function() # compile the AST to a python function
>>> fn(1, 2) # run the function


This module isn’t intended as a way to construct circuits directly. Instead, build diagrams with polycirc.ir, and compile them to the AST form with diagram_to_ast().

In more detail:

  • diagram_to_ast() converts a Diagram to an abstract syntax tree whose top level node is a FunctionDefinition.

  • FunctionDefinition is an AST with:
    • a name …

    • a list of named inputs…

    • … outputs

    • and a list of internal Assignment statements

  • A FunctionDefinition a can be compiled to a callable python function with a.to_function().

  • diagram_to_function() is a helper function to compile a diagram to a function directly

Note that in contrast to a usual AST representation, the classes in this module are not recursive. The role normally played by recursion is instead handled by the combinatorial structure of Yarrow’s Diagram datastructure, which might be thought of as a kind of “flattened” AST. The AST representation here is correspondingly “flat”: a FunctionDefinition is essentially just a list of assignment statements of the form var = var | constant | binary op | unary op, and operations can only refer to names or constants.

class polycirc.ast.ASTNode

The base class of AST nodes

class polycirc.ast.Name(id: str)

A named variable

id: str
class polycirc.ast.UnaryOp(op: Operation, rhs: Name | Constant)

Unary operations (like negation -x)

op: Operation
rhs: Name | Constant
class polycirc.ast.BinOp(lhs: Name | Constant, op: Operation, rhs: Name | Constant)

A binary operation

lhs: Name | Constant
op: Operation
rhs: Name | Constant
class polycirc.ast.Expr

Expression nodes evaluate to a single value. Note that Expr is not recursive: nested operations like x0 = (x1 + x2) + x3 cannot be represented.

value: Name | BinOp | UnaryOp | Constant
class polycirc.ast.Assignment(lhs: Name, rhs: Expr)

An Assignment sets a variable’s value to an expression. For example x₀ = x₁ * 2

lhs: Name
rhs: Expr
class polycirc.ast.FunctionDefinition(function_name: Name, args: List[Name], body: List[Assignment], returns: List[Name])

The top-level node of an expression tree. This essentially wraps a list of assigments of expressions to variables.

>>> from polycirc.operation import Add
>>> name = 'foo'
>>> x0, x1, x2 = [ Name(x) for x in ["x0", "x1", "x2"] ]
>>> args = [x0, x1]
>>> body = [Assignment(x2, BinOp(x0, Add(), x1))]
>>> returns = [x2]
>>> print(FunctionDefinition(name, args, body, returns), end='')
def foo(x0, x1):
    x2 = x0 + x1
    return [x2]
function_name: Name
args: List[Name]
body: List[Assignment]
returns: List[Name]

Use python’s ‘compile’ to turn this AST into a python function

polycirc.ast.make_name(i: int)

Turn an integer i into the string "x{i}"

>>> make_name(2)
polycirc.ast.op_to_assignments(op: Operation, args, coargs) List[Assignment]

Convert an Operation and its source and target hypernodes into a list of Assignment statements

polycirc.ast.diagram_to_ast(d: Diagram, function_name: str) FunctionDefinition

Turn a yarrow Diagram into a FunctionDefinition by decomposing it acyclically

polycirc.ast.diagram_to_function(d: Diagram, function_name: str = 'fn')

Turn a diagram directly into a callable python function.