graph – Interface for the PyTensor graph#

Core graph classes.

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

A Node representing the application of an operation to inputs.

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 pytensor.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.

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.

op[source]#

The operation that produces outputs given inputs.

inputs[source]#

The arguments of the expression modeled by the Apply node.

outputs[source]#

The outputs of the expression modeled by the Apply node.

clone(clone_inner_graph=False)[source]#

Clone this Apply instance.

Parameters:

clone_inner_graph – If True, clone HasInnerGraph Ops and their inner-graphs.

Return type:

A new Apply instance with new outputs.

Notes

Tags are copied from self to the returned instance.

clone_with_new_inputs(inputs, strict=True, clone_inner_graph=False)[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 TensorType 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).

  • clone_inner_graph (bool) – If True, clone HasInnerGraph Ops and their inner-graphs.

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]#

The number of inputs.

property nout[source]#

The number of outputs.

property out[source]#

An alias for self.default_output

class pytensor.graph.basic.AtomicVariable(type, name=None, **kwargs)[source]#

A node type that has no ancestors and should never be considered an input to a graph.

clone(**kwargs)[source]#

Return a new, un-owned Variable like self.

Parameters:

**kwargs (dict) – Optional “name” keyword argument for the copied instance. Same as self.name if value not provided.

Returns:

A new Variable instance with no owner or index.

Return type:

Variable instance

Notes

Tags and names are copied to the returned instance.

equals(other)[source]#

This does what __eq__ would normally do, but Variable and Apply should always be hashable by id.

class pytensor.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(**kwargs)[source]#

Return a new, un-owned Variable like self.

Parameters:

**kwargs (dict) – Optional “name” keyword argument for the copied instance. Same as self.name if value not provided.

Returns:

A new Variable instance with no owner or index.

Return type:

Variable instance

Notes

Tags and names are copied to the returned instance.

get_test_value()[source]#

Get the test value.

Raises:

TestValueError

class pytensor.graph.basic.Node[source]#

A Node in an PyTensor 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.

dprint(**kwargs)[source]#

Debug print itself

Parameters:

kwargs – Optional keyword arguments to pass to debugprint function.

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 pytensor.graph.basic.NominalVariable(id, typ, **kwargs)[source]#

A variable that enables alpha-equivalent comparisons.

clone(**kwargs)[source]#

Return a new, un-owned Variable like self.

Parameters:

**kwargs (dict) – Optional “name” keyword argument for the copied instance. Same as self.name if value not provided.

Returns:

A new Variable instance with no owner or index.

Return type:

Variable instance

Notes

Tags and names are copied to the returned instance.

class pytensor.graph.basic.Variable(type, owner, 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.

  • 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 pytensor
import pytensor.tensor as pt

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

c = a + b  # create a simple expression

f = pytensor.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

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

pytensor.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(**kwargs)[source]#

Return a new, un-owned Variable like self.

Parameters:

**kwargs (dict) – Optional “name” keyword argument for the copied instance. Same as self.name if value not provided.

Returns:

A new Variable instance with no owner or index.

Return type:

Variable instance

Notes

Tags and names are copied to the returned instance.

eval(inputs_to_values=None, **kwargs)[source]#

Evaluate the Variable given a set of values for its inputs.

Parameters:
  • inputs_to_values – A dictionary mapping PyTensor Variables or names to values. Not needed if variable has no required inputs.

  • kwargs – Optional keyword arguments to pass to the underlying pytensor.function

Examples

>>> import numpy as np
>>> import pytensor.tensor as pt
>>> x = pt.dscalar("x")
>>> y = pt.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 PyTensor 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 PyTensor 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

pytensor.graph.basic.as_string(inputs, outputs, leaf_formatter=<class 'str'>, node_formatter=<function default_node_formatter>)[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

pytensor.graph.basic.clone(inputs, outputs, copy_inputs=True, copy_orphans=None, clone_inner_graphs=False)[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.

  • clone_inner_graphs (bool) – If True, clone HasInnerGraph Ops and their inner-graphs.

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.

pytensor.graph.basic.clone_get_equiv(inputs, outputs, copy_inputs=True, copy_orphans=True, memo=None, clone_inner_graphs=False, **kwargs)[source]#

Clone the graph between inputs and outputs and return a map of the cloned objects.

This function works by recursively cloning inputs and rebuilding a directed graph from the inputs up.

If memo already contains entries for some of the objects in the graph, those objects are replaced with their values in memo and not unnecessarily cloned.

Parameters:
  • inputs – Inputs of the graph to be cloned.

  • outputs – Outputs of the graph to be cloned.

  • copy_inputsTrue means to create the cloned graph from cloned input nodes. False means to clone a graph that is rooted at the original input nodes. Constants are not cloned.

  • copy_orphans – When True, inputs with no owners are cloned. When False, original inputs are reused in the new graph. Cloning is not performed for Constants.

  • memo – 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.

  • clone_inner_graphs – If True, clone HasInnerGraph Ops and their inner-graphs.

  • kwargs – Keywords passed to Apply.clone_with_new_inputs.

pytensor.graph.basic.clone_node_and_cache(node, clone_d, clone_inner_graphs=False, **kwargs)[source]#

Clone an Apply node and cache the results in clone_d.

This function handles Op clones that are generated by inner-graph cloning.

Returns:

  • None if all of node’s outputs are already in clone_d; otherwise,

  • return the clone of node.

pytensor.graph.basic.equal_computations(xs, ys, in_xs=None, in_ys=None, strict_dtype=True)[source]#

Checks if PyTensor 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:
Return type:

bool

pytensor.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