graph – Interface for the Aesara graph

Reference

Core graph classes.

class aesara.graph.basic.Apply(op, inputs, outputs)[source]

A Node representing the application of an operation to inputs.

An Apply instance serves as a simple structure with three important attributes:

  • inputs : a list of Variable nodes that represent the arguments of the expression,
  • outputs : a list of Variable nodes that represent the computed outputs of the expression, and
  • op : an Op instance that determines the nature of the expression being applied.

Basically, an Apply instance is an object that represents the Python statement outputs = op(*inputs).

This class is typically instantiated by a Op.make_node method, which is called by Op.__call__.

The function aesara.compile.function.function uses Apply.inputs together with Variable.owner to search the expression graph and determine which inputs are necessary to compute the function’s outputs.

A Linker uses the Apply instance’s op field to compute numeric values for the output variables.

Parameters:
  • op (A Op instance) –
  • inputs (list of Variable instances) –
  • outputs (list of Variable instances) –

Notes

The Variable.owner field of each Apply.outputs element is set to self in Apply.make_node.

If an output element has an owner that is neither None nor self, then a ValueError exception will be raised.

clone()[source]

Duplicate this Apply instance with inputs = self.inputs.

Returns:A new Apply instance (or subclass instance) with new outputs.
Return type:object

Notes

Tags are copied from self to the returned instance.

clone_with_new_inputs(inputs, strict=True)[source]

Duplicate this Apply instance in a new graph.

Parameters:
  • inputs (list of Variables) – List of Variable instances to use as inputs.
  • strict (bool) – If True, the type fields of all the inputs must be equal to the current ones (or compatible, for instance Tensor / GpuArray of the same dtype and broadcastable patterns, in which case they will be converted into current Type), and returned outputs are guaranteed to have the same types as self.outputs. If False, then there’s no guarantee that the clone’s outputs will have the same types as self.outputs, and cloning may not even be possible (it depends on the Op).
Returns:

An Apply instance with the same Op but different outputs.

Return type:

object

default_output()[source]

Returns the default output for this node.

Returns:An element of self.outputs, typically self.outputs[0].
Return type:Variable instance

Notes

May raise AttributeError self.op.default_output is out of range, or if there are multiple outputs and self.op.default_output does not exist.

get_parents()[source]

Return a list of the parents of this node. Should return a copy–i.e., modifying the return value should not modify the graph structure.

property nin[source]

Number of inputs.

Type:Property
property nout[source]

Number of outputs.

Type:Property
property out[source]

Alias for self.default_output().

property params_type[source]

type to use for the params

run_params()[source]

Returns the params for the node, or NoParams if no params is set.

class aesara.graph.basic.Constant(type, data, name=None)[source]

A Variable with a fixed data field.

Constant nodes make numerous optimizations possible (e.g. constant in-lining in C code, constant folding, etc.)

Notes

The data field is filtered by what is provided in the constructor for the Constant’s type field.

clone()[source]

Create a shallow clone.

We clone this object, but we don’t clone the data to lower memory requirement. We suppose that the data will never change.

get_test_value()[source]

Get the test value.

Raises:TestValueError
property value[source]

read-only data access method

class aesara.graph.basic.Node[source]

A Node in an Aesara graph.

Currently, graphs contain two kinds of Nodes: Variables and Applys. Edges in the graph are not explicitly represented. Instead each Node keeps track of its parents via Variable.owner / Apply.inputs.

get_parents()[source]

Return a list of the parents of this node. Should return a copy–i.e., modifying the return value should not modify the graph structure.

class aesara.graph.basic.Variable(type, owner=None, index=None, name=None)[source]

A Variable is a node in an expression graph that represents a variable.

The inputs and outputs of every Apply are Variable instances. The input and output arguments to create a function are also Variable instances. A Variable is like a strongly-typed variable in some other languages; each Variable contains a reference to a Type instance that defines the kind of value the Variable can take in a computation.

A Variable is a container for four important attributes:

  • type a Type instance defining the kind of value this Variable can have,
  • owner either None (for graph roots) or the Apply instance of which self is an output,
  • index the integer such that owner.outputs[index] is this_variable (ignored if owner is None),
  • name a string to use in pretty-printing and debugging.

There are a few kinds of Variables to be aware of: A Variable which is the output of a symbolic computation has a reference to the Apply instance to which it belongs (property: owner) and the position of itself in the owner’s output list (property: index).

  • Variable (this base type) is typically the output of a symbolic computation.
  • Constant: a subclass which adds a default and un-replaceable value, and requires that owner is None.
  • TensorVariable subclass of Variable that represents a numpy.ndarray
    object.
  • TensorSharedVariable: a shared version of TensorVariable.
  • SparseVariable: a subclass of Variable that represents a scipy.sparse.{csc,csr}_matrix object.
  • GpuArrayVariable: a subclass of Variable that represents our object on the GPU that is a subset of numpy.ndarray.
  • RandomVariable.

A Variable which is the output of a symbolic computation will have an owner not equal to None.

Using a Variables’ owner field and an Apply node’s inputs fields, one can navigate a graph from an output all the way to the inputs. The opposite direction is possible with a FunctionGraph and its FunctionGraph.clients dict, which maps Variables to a list of their clients.

Parameters:
  • type (a Type instance) – The type governs the kind of data that can be associated with this variable.
  • owner (None or Apply instance) – The Apply instance which computes the value for this variable.
  • index (None or int) – The position of this Variable in owner.outputs.
  • name (None or str) – A string for pretty-printing and debugging.

Examples

import aesara
import aesara.tensor as at

a = at.constant(1.5)            # declare a symbolic constant
b = at.fscalar()                # declare a symbolic floating-point scalar

c = a + b                       # create a simple expression

f = aesara.function([b], [c])   # this works because a has a value associated with it already

assert 4.0 == f(2.5)            # bind 2.5 to an internal copy of b and evaluate an internal c

aesara.function([a], [c])       # compilation error because b (required by c) is undefined

aesara.function([a,b], [c])     # compilation error because a is constant, it can't be an input

The python variables a, b, c all refer to instances of type Variable. The Variable referred to by a is also an instance of Constant.

clone()[source]

Return a new Variable like self.

Returns:A new Variable instance (or subclass instance) with no owner or index.
Return type:Variable instance

Notes

Tags are copied to the returned instance.

Name is copied to the returned instance.

eval(inputs_to_values=None)[source]

Evaluate the Variable.

Parameters:inputs_to_values – A dictionary mapping Aesara Variables to values.

Examples

>>> import numpy as np
>>> import aesara.tensor as at
>>> x = at.dscalar('x')
>>> y = at.dscalar('y')
>>> z = x + y
>>> np.allclose(z.eval({x : 16.3, y : 12.1}), 28.4)
True

We passed eval() a dictionary mapping symbolic Aesara Variables to the values to substitute for them, and it returned the numerical value of the expression.

Notes

eval() will be slow the first time you call it on a variable – it needs to call function() to compile the expression behind the scenes. Subsequent calls to eval() on that same variable will be fast, because the variable caches the compiled function.

This way of computing has more overhead than a normal Aesara function, so don’t use it too much in real scripts.

get_parents()[source]

Return a list of the parents of this node. Should return a copy–i.e., modifying the return value should not modify the graph structure.

get_test_value()[source]

Get the test value.

Raises:TestValueError
aesara.graph.basic.ancestors(graphs: Iterable[aesara.graph.basic.Variable], blockers: Optional[Collection[aesara.graph.basic.Variable]] = None) Generator[aesara.graph.basic.Variable, None, None][source]

Return the variables that contribute to those in given graphs (inclusive).

Parameters:
  • graphs (list of Variable instances) – Output Variable instances from which to search backward through owners.
  • blockers (list of Variable instances) – A collection of Variables that, when found, prevent the graph search from preceding from that point.
Yields:

Variables – All input nodes, in the order found by a left-recursive depth-first search started at the nodes in graphs.

aesara.graph.basic.applys_between(ins: Collection[aesara.graph.basic.Variable], outs: Iterable[aesara.graph.basic.Variable]) Generator[aesara.graph.basic.Apply, None, None][source]

Extract the Applys contained within the sub-graph between given input and output variables.

Parameters:
Yields:
  • The Applys that are contained within the sub-graph that lies
  • between ins and outs, including the owners of the Variables in
  • outs and intermediary Applys between ins and outs, but not the
  • owners of the Variables in ins.
aesara.graph.basic.as_string(inputs: List[aesara.graph.basic.Variable], outputs: List[aesara.graph.basic.Variable], leaf_formatter=<class 'str'>, node_formatter=<function default_node_formatter>) List[str][source]

Returns a string representation of the subgraph between inputs and outputs.

Parameters:
  • inputs (list) – Input Variables.
  • outputs (list) – Output Variables.
  • leaf_formatter (callable) – Takes a Variable and returns a string to describe it.
  • node_formatter (callable) – Takes an Op and the list of strings corresponding to its arguments and returns a string to describe it.
Returns:

Returns a string representation of the subgraph between inputs and outputs. If the same node is used by several other nodes, the first occurrence will be marked as *n -> description and all subsequent occurrences will be marked as *n, where n is an id number (ids are attributed in an unspecified order and only exist for viewing convenience).

Return type:

list of str

aesara.graph.basic.clone(inputs: List[aesara.graph.basic.Variable], outputs: List[aesara.graph.basic.Variable], copy_inputs: bool = True, copy_orphans: Optional[bool] = None) Tuple[Collection[aesara.graph.basic.Variable], Collection[aesara.graph.basic.Variable]][source]

Copies the sub-graph contained between inputs and outputs.

Parameters:
  • inputs – Input Variables.
  • outputs – Output Variables.
  • copy_inputs – If True, the inputs will be copied (defaults to True).
  • copy_orphans – When None, use the copy_inputs value. When True, new orphans nodes are created. When False, original orphans nodes are reused in the new graph.
Returns:

Return type:

The inputs and outputs of that copy.

Notes

A constant, if in the inputs list is not an orphan. So it will be copied conditional on the copy_inputs parameter; otherwise, it will be copied conditional on the copy_orphans parameter.

aesara.graph.basic.clone_get_equiv(inputs: List[aesara.graph.basic.Variable], outputs: List[aesara.graph.basic.Variable], copy_inputs: bool = True, copy_orphans: bool = True, memo: Optional[Dict[aesara.graph.basic.Variable, aesara.graph.basic.Variable]] = None)[source]

Return a dictionary that maps from Variable and Apply nodes in the original graph to a new node (a clone) in a new graph.

This function works by recursively cloning inputs… rebuilding a directed graph from the inputs up to eventually building new outputs.

Parameters:
  • inputs (a list of Variables) –
  • outputs (a list of Variables) –
  • copy_inputs (bool) – True means to create the cloned graph from new input nodes (the bottom of a feed-upward graph). False means to clone a graph that is rooted at the original input nodes.
  • copy_orphans – When True, new constant nodes are created. When False, original constant nodes are reused in the new graph.
  • memo (None or dict) – Optionally start with a partly-filled dictionary for the return value. If a dictionary is passed, this function will work in-place on that dictionary and return it.
aesara.graph.basic.clone_replace(output: Collection[aesara.graph.basic.Variable], replace: Optional[Dict[aesara.graph.basic.Variable, aesara.graph.basic.Variable]] = None, strict: bool = True, share_inputs: bool = True) Collection[aesara.graph.basic.Variable][source]

Clone a graph and replace subgraphs within it.

It returns a copy of the initial subgraph with the corresponding substitutions.

Parameters:
  • output (Aesara Variables (or Aesara expressions)) – Aesara expression that represents the computational graph.
  • replace (dict) – Dictionary describing which subgraphs should be replaced by what.
  • share_inputs (bool) – If True, use the same inputs (and shared variables) as the original graph. If False, clone them. Note that cloned shared variables still use the same underlying storage, so they will always have the same value.
aesara.graph.basic.equal_computations(xs, ys, in_xs=None, in_ys=None)[source]

Checks if Aesara graphs represent the same computations.

The two lists xs, ys should have the same number of entries. The function checks if for any corresponding pair (x, y) from zip(xs, ys) x and y represent the same computations on the same variables (unless equivalences are provided using in_xs, in_ys).

If in_xs and in_ys are provided, then when comparing a node x with a node y they are automatically considered as equal if there is some index i such that x == in_xs[i] and y == in_ys[i] (and they both have the same type). Note that x and y can be in the list xs and ys, but also represent subgraphs of a computational graph in xs or ys.

Parameters:
  • xs (list of Variable) –
  • ys (list of Variable) –
Returns:

Return type:

bool

aesara.graph.basic.general_toposort(outputs: Iterable[aesara.graph.basic.T], deps: Callable[[aesara.graph.basic.T], Union[aesara.misc.ordered_set.OrderedSet, List[aesara.graph.basic.T]]], compute_deps_cache: Optional[Callable[[aesara.graph.basic.T], Union[aesara.misc.ordered_set.OrderedSet, List[aesara.graph.basic.T]]]] = None, deps_cache: Optional[Dict[aesara.graph.basic.T, List[aesara.graph.basic.T]]] = None, clients: Optional[Dict[aesara.graph.basic.T, List[aesara.graph.basic.T]]] = None) List[aesara.graph.basic.T][source]

Perform a topological sort of all nodes starting from a given node.

Parameters:
  • deps (callable) – A Python function that takes a node as input and returns its dependence.
  • compute_deps_cache (optional) – If provided, deps_cache should also be provided. This is a function like deps, but that also caches its results in a dict passed as deps_cache.
  • deps_cache (dict) – A dict mapping nodes to their children. This is populated by compute_deps_cache.
  • clients (dict) – If a dict is passed, it will be filled with a mapping of nodes-to-clients for each node in the subgraph.

Notes

deps(i) should behave like a pure function (no funny business with internal state).

deps(i) will be cached by this function (to be fast).

The order of the return value list is determined by the order of nodes returned by the deps function.

The second option removes a Python function call, and allows for more specialized code, so it can be faster.

aesara.graph.basic.get_var_by_name(graphs: Iterable[aesara.graph.basic.Variable], target_var_id: str, ids: str = 'CHAR') Tuple[aesara.graph.basic.Variable][source]

Get variables in a graph using their names.

Parameters:
  • graphs – The graph, or graphs, to search.
  • target_var_id – The name to match against either Variable.name or Variable.auto_name.
Returns:

Return type:

A tuple containing all the Variables that match target_var_id.

aesara.graph.basic.graph_inputs(graphs: Iterable[aesara.graph.basic.Variable], blockers: Optional[Collection[aesara.graph.basic.Variable]] = None) Generator[aesara.graph.basic.Variable, None, None][source]

Return the inputs required to compute the given Variables.

Parameters:
  • graphs (list of Variable instances) – Output Variable instances from which to search backward through owners.
  • blockers (list of Variable instances) – A collection of Variables that, when found, prevent the graph search from preceding from that point.
Yields:
  • Input nodes with no owner, in the order found by a left-recursive
  • depth-first search started at the nodes in graphs.
aesara.graph.basic.io_connection_pattern(inputs, outputs)[source]

Return the connection pattern of a subgraph defined by given inputs and outputs.

aesara.graph.basic.io_toposort(inputs: List[aesara.graph.basic.Variable], outputs: List[aesara.graph.basic.Variable], orderings: Optional[Dict[aesara.graph.basic.Apply, List[aesara.graph.basic.Apply]]] = None, clients: Optional[Dict[aesara.graph.basic.Variable, List[aesara.graph.basic.Variable]]] = None) List[aesara.graph.basic.Apply][source]

Perform topological sort from input and output nodes.

Parameters:
  • inputs (list or tuple of Variable instances) – Graph inputs.
  • outputs (list or tuple of Apply instances) – Graph outputs.
  • orderings (dict) – Keys are Apply instances, values are lists of Apply instances.
  • clients (dict) – If provided, it will be filled with mappings of nodes-to-clients for each node in the subgraph that is sorted.
aesara.graph.basic.is_in_ancestors(l_apply: aesara.graph.basic.Apply, f_node: aesara.graph.basic.Apply) bool[source]

Determine if f_node is in the graph given by l_apply.

Parameters:
  • l_apply (Apply) – The node to walk.
  • f_apply (Apply) – The node to find in l_apply.
Returns:

Return type:

bool

aesara.graph.basic.list_of_nodes(inputs: Collection[aesara.graph.basic.Variable], outputs: Iterable[aesara.graph.basic.Variable]) List[aesara.graph.basic.Apply][source]

Return the Apply nodes of the graph between inputs and outputs.

Parameters:
  • inputs (list of Variable) – Input Variables.
  • outputs (list of Variable) – Output Variables.
aesara.graph.basic.nodes_constructed()[source]

A context manager that is used in inherit_stack_trace and keeps track of all the newly created variable nodes inside an optimization. A list of new_nodes is instantiated but will be filled in a lazy manner (when Variable.notify_construction_observers is called).

observer is the entity that updates the new_nodes list. construction_observers is a list inside Variable class and contains a list of observer functions. The observer functions inside construction_observers are only called when a Variable is instantiated (where Variable.notify_construction_observers is called). When the observer function is called, a new Variable is added to the new_nodes list.

Parameters:
  • new_nodes – A list of all the Variables that are created inside the optimization.
  • yieldsnew_nodes list.
aesara.graph.basic.op_as_string(i, op, leaf_formatter=<class 'str'>, node_formatter=<function default_node_formatter>)[source]

Return a function that returns a string representation of the subgraph between i and op.inputs

aesara.graph.basic.orphans_between(ins: Collection[aesara.graph.basic.Variable], outs: Iterable[aesara.graph.basic.Variable]) Generator[aesara.graph.basic.Variable, None, None][source]

Extract the Variables not within the sub-graph between input and output nodes.

Parameters:
Yields:

Variable – The Variables upon which one or more Variables in outs depend, but are neither in ins nor in the sub-graph that lies between them.

Examples

>>> orphans_between([x], [(x+y).out])
[y]
aesara.graph.basic.vars_between(ins: Collection[aesara.graph.basic.Variable], outs: Iterable[aesara.graph.basic.Variable]) Generator[aesara.graph.basic.Variable, None, None][source]

Extract the Variables within the sub-graph between input and output nodes.

Parameters:
Yields:

Variables – The Variables that are involved in the subgraph that lies between ins and outs. This includes ins, outs, orphans_between(ins, outs) and all values of all intermediary steps from ins to outs.

aesara.graph.basic.view_roots(node: aesara.graph.basic.Variable) List[aesara.graph.basic.Variable][source]

Return the leaves from a search through consecutive view-maps.

aesara.graph.basic.walk(nodes: Iterable[aesara.graph.basic.T], expand: Callable[[aesara.graph.basic.T], Optional[Sequence[aesara.graph.basic.T]]], bfs: bool = True, return_children: bool = False, hash_fn: Callable[[aesara.graph.basic.T], Hashable] = <built-in function id>) Generator[aesara.graph.basic.T, None, Dict[aesara.graph.basic.T, List[aesara.graph.basic.T]]][source]

Walk through a graph, either breadth- or depth-first.

Parameters:
  • nodes (deque) – The nodes from which to start walking.
  • expand (callable) – A callable that is applied to each node in nodes, the results of which are either new nodes to visit or None.
  • bfs (bool) – If True, breath first search is used; otherwise, depth first search.
  • return_children (bool) – If True, each output node will be accompanied by the output of expand (i.e. the corresponding child nodes).
  • hash_fn (callable) – The function used to produce hashes of the elements in nodes. The default is id.
Yields:

nodes

Notes

A node will appear at most once in the return value, even if it appears multiple times in the nodes parameter.