circkit.operation

Operations may include arbitrary parameters. It is important to distinguish parameters (arbitrary objects) from node’s inputs (other nodes in the same circuit).

An Operation (sub)class describes an operation and all its parameters, including validation of parameters, number of node’s inputs and outputs.

An Operation instance describes a concrete operation with all parameters set. It does not however define a node in a circuit or node’s inputs/outputs. One Operation instance can be used by multiple nodes.

The node/operation creation API is a double-step call (similar to e.g. Keras Layers API):

x = circuit.INPUT("x")()
xcube = circuit.EXP(3)(x)

assert issubclass(circuit.EXP, Operation)
assert isinstance(circuit.EXP(3), Operation)
assert isinstance(circuit.EXP(3)(x), circuit.Node)

Here, both lines follow this API, in which the first call creates an Operation instance and the second call creates a circkit.node.Node instance.

In the first line, we first create the Circuit.INPUT operation, which has the input name “x” as the only parameter: circuit.INPUT("x"). Then, we call this operation without any incoming nodes to create a new node in the circuit, corresponding to the input “x”.

In the second line, we first create the EXP operation with the parameter power = 3. We then call this operation with a node as an argument which defines the incoming node for exponentitation.

The reason for the double-call is to separate clearly the two different notions - parameters and inputs, and to allow using the full python’s call syntax. For example, we could use keyword args such as Circuit.EXP(power=3). This is particularly useful for complex operations to reduce the number of errors e.g. in parameter/input order.

Defining operation example:

class NewCircuitType(Circuit):
    class Operations(Circuit.Operations):  # subclass to include
                                           # standard INPUT, GET, CONST
        class ADD(Operation.Binary):
            def eval(self, a, b):
                return a + b

        class SUB(Operation.Binary):
            SYMMETRIC = False
            def eval(self, a, b):
                return a - b

        class MIX(Operation.MultiVariadic):
            alpha : Param.Int(min_value=1) = 2

            def determine_n_outputs(self, node):
                return len(node.incoming)

            def eval(self, *args):
                t = self.alpha * sum(args)
                return [a - t for a in args]

Module contents

circkit.operation.VARIABLE = <VARIABLE>

Special Operation.n_inputs / Operation.n_outputs value - unspecified number of inputs/outputs (or determined dynamically).

circkit.operation.UNIT = <UNIT>

Special Operation.n_outputs value - non-iterable output.

class circkit.operation.OperationMeta(clsname, bases, attrs)

Bases: type

Metaclass for the Operation class.

Manages definition of operations (classes) and creation of operation instances (e.g. preparing parameters, caching operatios).

static __new__(meta, clsname, bases, attrs)

Define a new Operation class (in a circuit class).

  1. Parse definitions of parameters from the annotations and assignments (defaults).

  2. Update __slots__ to include new parameters.

  3. Collects parameters from superclasses. If such a parameter attribute is overriden by a non-parameter attribute, the parameter is excluded. This allows e.g. to remove superclass’ parameter by setting attribute param = None.

  4. Create dynamically new Operation class with given slots and parameters descriptions.

__call__(*values, **kvalues)

Create an instance of Operation.

Currently, only implements caching of Operation`s (if enabled), and passes all the parameters to the constructor of the :class:`Operation.

class circkit.operation.Operation(*values, **kvalues)

Bases: object

Describes a (parametrized) operation that can be used in a circuit.

Parameters of an Operation instance are accessible directly as attributes.

All supporting attributes are either prefixed with “_” or are UPPERCASE to avoid collisions with possible parameter names. Exceptions: n_inputs and n_outputs.

Defining attributes (may/should be overwritten in subclasses)
n_inputs

Number of node inputs the operation requires.

Type

int

n_outputs

Number of node outputs the operation has (to be retrieved with the Circuit.Operations.GET operation). By default, is set to the special object UNIT, which means that the output is non iterable. Note that this is different from n_outputs = 1, where the actual output has to be retrieved e.g. as out = node[0] or out, = node.

Type

int = UNIT

SYMMETRIC

Whether the order of the input nodes does not matter. Used e.g. for caching nodes (b + a may return the cached node a + b).

Type

bool

PRECOMPUTABLE

Whether the operation is precomputable (given the inputs and the parameters).

Type

bool

STR_LIMIT

Maximum length of the string describing parameters to keep (for __str__()).

Type

int = 30

Functional attributes (should not be overriden manually)
_circuit

Circuit instance in which this Operation class/instance is defined/subclassed.

Type

Circuit

_circuit_class

Circuit class in which this Operation class/instance is defined/subclassed.

Type

Circuit

_param_descriptions

Mapping from parameter names to Param objects describing the parameters.

Type

dict`[str, :class:.Param`]

__init__(*values, **kvalues)

Create an Operation instance by specifying parameters (if any).

Note that it is not linked to any Node yet.

__call__(*incoming, **kwargs)

Create a new node using this operation.

reapply(circuit, name=None)

Create new operation instance with same parameters (by name), but in the other circuit. @name define name of the operation, by default it is the name of this operation.

__eq__(other)

Test equality of two operations.

Two operations are equal if their names are equal and all parameters are equal.

To check the class of the operation (e.g. INPUT, ADD, CONST, etc.) use:

  • isinstance(op, CircuitClass.ADD) : checks circuit class;

  • isinstance(op, CircuitInstance.ADD) : checks circuit instance too;

  • op.is_ADD() : ADD must be present in the circuit’s class;

  • op._name == "ADD" : by name, does not check circuit class/instance.

Parameters

other (Operation) –

__hash__()

Return hash(self).

__str__()

Return str(self).

__repr__()

Return repr(self).

eval_with_node(node, *args)

This method should be overriden if the evaluation requires some information from the node. By default, it ignores the node and calls eval().

eval(*args)

Evaluate the operation on given inputs (typically values, not nodes).

before_create_node(*args)

Validate node inputs before creating a new node with this operation.

after_create_node(node)

Check new node after creation (node uses this operation).

classmethod on_new_circuit(circuit)

Callback on linking the operation class to a new circuit instance.

E.g. can store common circuit-level data in the circuit instance.

determine_n_outputs(node)

Determine number of outputs of a new node using this operation.

By default, returns operation’s n_outputs. However, this method must be overriden for multi-operations. For example, the number of outputs may be set equal to the number of inputs.

Return type

int

__reduce__()

Helper for pickle.

__setstate__(src)

To update class.__dict__ with dict/map

class Binary(*values, **kvalues)

Bases: Operation

Operation with 2 inputs.

class MultiBinary(*values, **kvalues)

Bases: Operation

Operation with 2 inputs and (possibly) multiple number of outputs.

class MultiNullary(*values, **kvalues)

Bases: Operation

Operation with no inputs and (possibly) multiple number of outputs.

class MultiTernary(*values, **kvalues)

Bases: Operation

Operation with 3 inputs and (possibly) multiple number of outputs.

class MultiUnary(*values, **kvalues)

Bases: Operation

Operation with 1 input and (possibly) multiple number of outputs.

class MultiVariadic(*values, **kvalues)

Bases: Operation

Operation with variable number of inputs and (possibly) multiple number of outputs.

class Nullary(*values, **kvalues)

Bases: Operation

Operation with no inputs.

class Ternary(*values, **kvalues)

Bases: Operation

Operation with 3 inputs.

class Unary(*values, **kvalues)

Bases: Operation

Operation with 1 inputs.

class Variadic(*values, **kvalues)

Bases: Operation

Operation with variable number of inputs.

class circkit.operation.Nullary(*values, **kvalues)

Bases: Operation

Operation with no inputs.

class circkit.operation.Unary(*values, **kvalues)

Bases: Operation

Operation with 1 inputs.

class circkit.operation.Binary(*values, **kvalues)

Bases: Operation

Operation with 2 inputs.

class circkit.operation.Ternary(*values, **kvalues)

Bases: Operation

Operation with 3 inputs.

class circkit.operation.Variadic(*values, **kvalues)

Bases: Operation

Operation with variable number of inputs.

class circkit.operation.MultiNullary(*values, **kvalues)

Bases: Operation

Operation with no inputs and (possibly) multiple number of outputs.

class circkit.operation.MultiUnary(*values, **kvalues)

Bases: Operation

Operation with 1 input and (possibly) multiple number of outputs.

class circkit.operation.MultiBinary(*values, **kvalues)

Bases: Operation

Operation with 2 inputs and (possibly) multiple number of outputs.

class circkit.operation.MultiTernary(*values, **kvalues)

Bases: Operation

Operation with 3 inputs and (possibly) multiple number of outputs.

class circkit.operation.MultiVariadic(*values, **kvalues)

Bases: Operation

Operation with variable number of inputs and (possibly) multiple number of outputs.

exception circkit.operation.UnhashableOperationError

Bases: Exception

Raised when an Operation is being hashed but hashing is not defined (e.g. some parameters are unhashable objects).

__weakref__

list of weak references to the object (if defined)