# `function` - defines aesara.function#

## Guide#

This module provides `function()`, commonly accessed as `aesara.function`, the interface for compiling graphs into callable objects.

You’ve already seen example usage in the basic tutorial… something like this:

```>>> import aesara
>>> x = aesara.tensor.dscalar()
>>> f = aesara.function([x], 2*x)
>>> f(4)
array(8.0)
```

The idea here is that we’ve compiled the symbolic graph (`2*x`) into a function that can be called on a number and will do some computations.

The behaviour of function can be controlled in several ways, such as `In`, `Out`, `mode`, `updates`, and `givens`.

## Computing More than one Thing at the Same Time#

Aesara supports functions with multiple outputs. For example, we can compute the element-wise difference, absolute difference, and squared difference between two matrices `a` and `b` at the same time:

```>>> a, b = at.dmatrices('a', 'b')
>>> diff = a - b
>>> abs_diff = abs(diff)
>>> diff_squared = diff**2
>>> f = aesara.function([a, b], [diff, abs_diff, diff_squared])
```

Note

`dmatrices` produces as many outputs as names that you provide. It is a shortcut for allocating symbolic variables that we will often use in the tutorials.

When we use the function `f`, it returns the three variables (the printing was reformatted for readability):

```>>> f([[1, 1], [1, 1]], [[0, 1], [2, 3]])
[array([[ 1.,  0.],
[-1., -2.]]), array([[ 1.,  0.],
[ 1.,  2.]]), array([[ 1.,  0.],
[ 1.,  4.]])]
```

## Setting a Default Value for an Argument#

Let’s say you want to define a function that adds two numbers, except that if you only provide one number, the other input is assumed to be one. You can do it like this:

```>>> from aesara.compile.io import In
>>> from aesara import function
>>> x, y = at.dscalars('x', 'y')
>>> z = x + y
>>> f = function([x, In(y, value=1)], z)
>>> f(33)
array(34.0)
>>> f(33, 2)
array(35.0)
```

This makes use of the In class which allows you to specify properties of your function’s parameters with greater detail. Here we give a default value of `1` for `y` by creating a `In` instance with its `value` field set to `1`.

Inputs with default values must follow inputs without default values (like Python’s functions). There can be multiple inputs with default values. These parameters can be set positionally or by name, as in standard Python:

```>>> x, y, w = at.dscalars('x', 'y', 'w')
>>> z = (x + y) * w
>>> f = function([x, In(y, value=1), In(w, value=2, name='w_by_name')], z)
>>> f(33)
array(68.0)
>>> f(33, 2)
array(70.0)
>>> f(33, 0, 1)
array(33.0)
>>> f(33, w_by_name=1)
array(34.0)
>>> f(33, w_by_name=1, y=0)
array(33.0)
```

Note

`In` does not know the name of the local variables `y` and `w` that are passed as arguments. The symbolic variable objects have name attributes (set by `dscalars` in the example above) and these are the names of the keyword parameters in the functions that we build. This is the mechanism at work in `In(y, value=1)`. In the case of ```In(w, value=2, name='w_by_name')```. We override the symbolic variable’s name attribute with a name to be used for this function.

You may like to see Function in the library for more detail.

## Copying functions#

Aesara functions can be copied, which can be useful for creating similar functions but with different shared variables or updates. This is done using the `aesara.compile.function.types.Function.copy()` method of `Function` objects. The optimized graph of the original function is copied, so compilation only needs to be performed once.

Let’s start from the accumulator defined above:

```>>> import aesara
>>> import aesara.tensor as at
>>> state = aesara.shared(0)
>>> inc = at.iscalar('inc')
>>> accumulator = aesara.function([inc], state, updates=[(state, state+inc)])
```

We can use it to increment the state as usual:

```>>> accumulator(10)
array(0)
>>> print(state.get_value())
10
```

We can use `copy()` to create a similar accumulator but with its own internal state using the `swap` parameter, which is a dictionary of shared variables to exchange:

```>>> new_state = aesara.shared(0)
>>> new_accumulator = accumulator.copy(swap={state:new_state})
>>> new_accumulator(100)
[array(0)]
>>> print(new_state.get_value())
100
```

The state of the first function is left untouched:

```>>> print(state.get_value())
10
```

We now create a copy with updates removed using the `delete_updates` parameter, which is set to `False` by default:

```>>> null_accumulator = accumulator.copy(delete_updates=True)
```

As expected, the shared state is no longer updated:

```>>> null_accumulator(9000)
[array(10)]
>>> print(state.get_value())
10
Reference
=========
```
class aesara.compile.function.In[source]#

A class for attaching information to function inputs.

variable[source]#

A variable in an expression graph to use as a compiled-function parameter

name[source]#

A string to identify an argument for this parameter in keyword arguments.

value[source]#

The default value to use at call-time (can also be a Container where the function will find a value at call-time.)

update[source]#

An expression which indicates updates to the Value after each function call.

mutable[source]#

`True` means the compiled-function is allowed to modify this argument. `False` means it is not allowed.

borrow[source]#

`True` indicates that a reference to internal storage may be returned, and that the caller is aware that subsequent function evaluations might overwrite this memory.

strict[source]#

If `False`, a function argument may be copied or cast to match the type required by the parameter `variable`. If `True`, a function argument must exactly match the type required by `variable`.

allow_downcast[source]#

`True` indicates that the value you pass for this input can be silently downcasted to fit the right type, which may lose precision. (Only applies when `strict` is `False`.)

autoname[source]#

`True` means that the `name` is set to variable.name.

implicit[source]#

`True` means that the input is implicit in the sense that the user is not allowed to provide a value for it. Requires ‘value’ to be set. `False` means that the user can provide a value for this input.

__init__(self, variable, name=None, value=None, update=None, mutable=None, strict=False, allow_downcast=None, autoname=True, implicit=None, borrow=None, shared=False)[source]#

Initialize attributes from arguments.

class aesara.compile.function.Out[source]#

A class for attaching information to function outputs

variable[source]#

A variable in an expression graph to use as a compiled-function output

borrow[source]#

`True` indicates that a reference to internal storage may be returned, and that the caller is aware that subsequent function evaluations might overwrite this memory.

__init__(variable, borrow=False)[source]#

Initialize attributes from arguments.

aesara.compile.function.function(inputs, outputs, mode=None, updates=None, givens=None, no_default_updates=False, accept_inplace=False, name=None, rebuild_strict=True, allow_input_downcast=None, profile=None, on_unused_input='raise')[source]#

Return a `callable object` that will calculate `outputs` from `inputs`.

Parameters:
• params (list of either Variable or In instances, but not shared variables.) – the returned `Function` instance will have parameters for these variables.

• outputs (list of Variables or Out instances) – expressions to compute.

• mode (None, string or `Mode` instance.) – compilation mode

• updates (iterable over pairs (shared_variable, new_expression). List, tuple or dict.) – expressions for new `SharedVariable` values

• givens (iterable over pairs (Var1, Var2) of Variables. List, tuple or dict. The Var1 and Var2 in each pair must have the same Type.) – specific substitutions to make in the computation graph (Var2 replaces Var1).

• no_default_updates (either bool or list of Variables) – if True, do not perform any automatic update on Variables. If False (default), perform them all. Else, perform automatic updates on all Variables that are neither in `updates` nor in `no_default_updates`.

• name – an optional name for this function. The profile mode will print the time spent in this function.

• rebuild_strict – True (Default) is the safer and better tested setting, in which case `givens` must substitute new variables with the same Type as the variables they replace. False is a you-better-know-what-you-are-doing setting, that permits `givens` to replace variables with new variables of any Type. The consequence of changing a Type is that all results depending on that variable may have a different Type too (the graph is rebuilt from inputs to outputs). If one of the new types does not make sense for one of the Ops in the graph, an Exception will be raised.

• allow_input_downcast (Boolean or None) – True means that the values passed as inputs when calling the function can be silently downcasted to fit the dtype of the corresponding Variable, which may lose precision. False means that it will only be cast to a more general, or precise, type. None (default) is almost like False, but allows downcasting of Python float scalars to floatX.

• profile (None, True, or ProfileStats instance) – accumulate profiling information into a given ProfileStats instance. If argument is `True` then a new ProfileStats instance will be used. This profiling object will be available via self.profile.

• on_unused_input – What to do if a variable in the ‘inputs’ list is not used in the graph. Possible values are ‘raise’, ‘warn’, and ‘ignore’.

Return type:

`Function` instance

Returns:

a callable object that will compute the outputs (given the inputs) and update the implicit function arguments according to the `updates`.

Inputs can be given as variables or `In` instances. `In` instances also have a variable, but they attach some extra information about how call-time arguments corresponding to that variable should be used. Similarly, `Out` instances can attach information about how output variables should be returned.

The default is typically ‘FAST_RUN’ but this can be changed in aesara.config. The mode argument controls the sort of rewrites that will be applied to the graph, and the way the rewritten graph will be evaluated.

After each function evaluation, the `updates` mechanism can replace the value of any (implicit) `SharedVariable` inputs with new values computed from the expressions in the `updates` list. An exception will be raised if you give two update expressions for the same `SharedVariable` input (that doesn’t make sense).

If a `SharedVariable` is not given an update expression, but has a `Variable.default_update` member containing an expression, this expression will be used as the update expression for this variable. Passing `no_default_updates=True` to `function` disables this behavior entirely, passing `no_default_updates=[sharedvar1, sharedvar2]` disables it for the mentioned variables.

Regarding givens: Be careful to make sure that these substitutions are independent, because behaviour when `Var1` of one pair appears in the graph leading to `Var2` in another expression is undefined (e.g. with `{a: x, b: a + 1}`). Replacements specified with givens are different from replacements that occur during normal rewriting, in that `Var2` is not expected to be equivalent to `Var1`.

aesara.compile.function.function_dump(filename, inputs, outputs=None, mode=None, updates=None, givens=None, no_default_updates=False, accept_inplace=False, name=None, rebuild_strict=True, allow_input_downcast=None, profile=None, on_unused_input=None, extra_tag_to_remove=None)[source]#

This is helpful to make a reproducible case for problems during Aesara compilation.

Ex:

replace `aesara.function(...)` by `aesara.function_dump('filename.pkl', ...)`.

If you see this, you were probably asked to use this function to help debug a particular case during the compilation of an Aesara function. `function_dump` allows you to easily reproduce your compilation without generating any code. It pickles all the objects and parameters needed to reproduce a call to `aesara.function()`. This includes shared variables and their values. If you do not want that, you can choose to replace shared variables values with zeros by calling set_value(…) on them before calling `function_dump`.

To load such a dump and do the compilation:

```>>> import pickle
>>> import aesara
>>> f = aesara.function(**d)
```

Note: The parameter `extra_tag_to_remove` is passed to the StripPickler used. To pickle graph made by Blocks, it must be: `['annotations', 'replacement_of', 'aggregation_scheme', 'roles']`

class aesara.compile.function.types.Function(vm: VM, input_storage, output_storage, indices, outputs, defaults, unpack_single: bool, return_none: bool, output_keys, maker: FunctionMaker, name: str | None = None)[source]#

A class that wraps the execution of a `VM` making it easier for use as a “function”.

`Function` is the callable object that does computation. It has the storage of inputs and outputs, performs the packing and unpacking of inputs and return values. It implements the square-bracket indexing so that you can look up the value of a symbolic node.

Functions are copyable via `Function.copy` and the `copy.copy` interface. When a function is copied, this instance is duplicated. Contrast with self.maker (instance of `FunctionMaker`) that is shared between copies. The meaning of copying a function is that the containers and their current values will all be duplicated. This requires that mutable inputs be copied, whereas immutable inputs may be shared between copies.

A Function instance is hashable, on the basis of its memory address (its id). A Function instance is only equal to itself. A Function instance may be serialized using the `pickle` or `cPickle` modules. This will save all default inputs, the graph, and WRITEME to the pickle file.

A `Function` instance has a `Function.trust_input` field that defaults to `False`. When `True`, the `Function` will skip all checks on the inputs.

finder[source]#

Dictionary mapping several kinds of things to containers.

We set an entry in finder for: - the index of the input - the variable instance the input is based on - the name of the input

All entries map to the container or to DUPLICATE if an ambiguity is detected.

inv_finder[source]#

Reverse lookup of `finder`. It maps containers to `SymbolicInput`s.

__call__(*args, **kwargs)[source]#

Evaluates value of a function on given arguments.

Parameters:
• args (list) – List of inputs to the function. All inputs are required, even when some of them are not necessary to calculate requested subset of outputs.

• kwargs (dict) –

The function inputs can be passed as keyword argument. For this, use the name of the input or the input instance as the key.

Keyword argument `output_subset` is a list of either indices of the function’s outputs or the keys belonging to the `output_keys` dict and represent outputs that are requested to be calculated. Regardless of the presence of `output_subset`, the updates are always calculated and processed. To disable the updates, you should use the `copy` method with `delete_updates=True`.

Returns:

List of outputs on indices/keys from `output_subset` or all of them, if `output_subset` is not passed.

Return type:

list

Copy this function. Copied function will have separated maker and fgraph with original function. User can choose whether to separate storage by changing the share_memory arguments.

Parameters:
• share_memory (boolean) – When True, two function share intermediate storages(storages except input and output storages). Otherwise two functions will only share partial storages and same maker. If two functions share memory and allow_gc=False, this will increase executing speed and save memory.

• swap (dict) – Dictionary that map old SharedVariables to new SharedVariables. Default is None. NOTE: The shared variable swap in only done in the new returned function, not in the user graph.

• delete_updates (boolean) – If True, Copied function will not have updates.

• name (string) – If provided, will be the name of the new Function. Otherwise, it will be old + “ copy”

• profile – as aesara.function profile parameter

Returns:

Copied aesara.Function

Return type:

aesara.Function

free()[source]#

When allow_gc = False, clear the Variables in storage_map