# Operator

`Operator(self, *args, **kwargs)`

Generate, JIT-compile and run C code starting from an ordered sequence of symbolic expressions.

## Parameters

Name | Type | Description | Default |
---|---|---|---|

`expressions` |
expr - like or list or expr - like | The (list of) expression(s) defining the Operator computation. | required |

`**kwargs` |
* name : str Name of the Operator, defaults to “Kernel”. * subs : dict Symbolic substitutions to be applied to `expressions` . * opt : str The performance optimization level. Defaults to `configuration['opt']` . * language : str The target language for shared-memory parallelism. Defaults to `configuration['language']` . * platform : str The architecture the code is generated for. Defaults to `configuration['platform']` . * compiler : str The backend compiler used to jit-compile the generated code. Defaults to `configuration['compiler']` . |
`{}` |

## Examples

The following Operator implements a trivial time-marching method that adds 1 to every grid point in `u`

at every timestep.

```
>>> from devito import Eq, Grid, TimeFunction, Operator
>>> grid = Grid(shape=(4, 4))
>>> u = TimeFunction(name='u', grid=grid)
>>> op = Operator(Eq(u.forward, u + 1))
```

Multiple expressions can be supplied, and there is no limit to the number of expressions in an Operator.

```
>>> v = TimeFunction(name='v', grid=grid)
>>> op = Operator([Eq(u.forward, u + 1),
+ 1)]) ... Eq(v.forward, v
```

Simple boundary conditions can be imposed easily exploiting the “indexed notation” for Functions/TimeFunctions.

```
>>> t = grid.stepping_dim
>>> x, y = grid.dimensions
>>> op = Operator([Eq(u.forward, u + 1),
+1, x, 0], 0),
... Eq(u[t+1, x, 2], 0),
... Eq(u[t+1, 0, y], 0),
... Eq(u[t+1, 2, y], 0)]) ... Eq(u[t
```

A semantically equivalent computation can be expressed exploiting SubDomains.

```
>>> u.data[:] = 0
>>> op = Operator(Eq(u.forward, u + 1, subdomain=grid.interior))
```

By specifying a SubDomain, the Operator constrains the execution of an expression to a certain sub-region within the computational domain. Ad-hoc SubDomains can also be created in application code – refer to the SubDomain documentation for more info.

Advanced boundary conditions can be expressed leveraging `SubDomain`

and `SubDimension`

.

Tensor contractions are supported, but with one caveat: in case of MPI execution, any global reductions along an MPI-distributed Dimension should be handled explicitly in user code. The following example shows how to implement the matrix-vector multiplication `Av = b`

(inducing a reduction along `y`

).

```
>>> from devito import Inc, Function
>>> A = Function(name='A', grid=grid)
>>> v = Function(name='v', shape=(3,), dimensions=(y,))
>>> b = Function(name='b', shape=(3,), dimensions=(x,))
>>> op = Operator(Inc(b, A*v))
```

Dense and sparse computation may be present within the same Operator. In the following example, interpolation is used to approximate the value of four sparse points placed at the center of the four quadrants at the grid corners.

```
>>> import numpy as np
>>> from devito import SparseFunction
>>> grid = Grid(shape=(4, 4), extent=(3.0, 3.0))
>>> f = Function(name='f', grid=grid)
>>> coordinates = np.array([(0.5, 0.5), (0.5, 2.5), (2.5, 0.5), (2.5, 2.5)])
>>> sf = SparseFunction(name='sf', grid=grid, npoint=4, coordinates=coordinates)
>>> op = Operator([Eq(f, f + 1)] + sf.interpolate(f))
```

The iteration direction is automatically detected by the Devito compiler. Below, the Operator runs from `time_M`

(maximum point in the time dimension) down to `time_m`

(minimum point in the time dimension), as opposed to all of the examples seen so far, in which the execution along time proceeds from `time_m`

to `time_M`

through unit-step increments.

`>>> op = Operator(Eq(u.backward, u + 1))`

Loop-level optimisations, including SIMD vectorisation and OpenMP parallelism, are automatically discovered and handled by the Devito compiler. For more information, refer to the relevant documentation.

## Attributes

Name | Description |
---|---|

cfunction | The JIT-compiled C function as a ctypes.FuncPtr object. |

## Methods

Name | Description |
---|---|

apply | Execute the Operator. |

arguments | Arguments to run the Operator. |

cinterface | Generate two files under the prescribed temporary directory: |

### apply

`apply(**kwargs)`

Execute the Operator.

With no arguments provided, the Operator runs using the data carried by the objects appearing in the input expressions – these are referred to as the “default arguments”.

Optionally, any of the Operator default arguments may be replaced by passing suitable key-value arguments. Given `apply(k=v, ...)`

, `(k, v)`

may be used to:

replace a Constant. In this case,

`k`

is the name of the Constant,`v`

is either a Constant or a scalar value.replace a Function (SparseFunction). Here,

`k`

is the name of the Function,`v`

is either a Function or a numpy.ndarray.alter the iteration interval along a Dimension. Consider a generic Dimension

`d`

iterated over by the Operator. By default, the Operator runs over all iterations within the compact interval`[d_m, d_M]`

, where`d_m`

and`d_M`

are, respectively, the smallest and largest integers not causing out-of-bounds memory accesses (for the Grid Dimensions, this typically implies iterating over the entire physical domain). So now`k`

can be either`d_m`

or`d_M`

, while`v`

is an integer value.

#### Examples

Consider the following Operator

```
>>> from devito import Eq, Grid, TimeFunction, Operator
>>> grid = Grid(shape=(3, 3))
>>> u = TimeFunction(name='u', grid=grid, save=3)
>>> op = Operator(Eq(u.forward, u + 1))
```

The Operator is run by calling `apply`

`>>> summary = op.apply()`

The variable `summary`

contains information about runtime performance. As no key-value parameters are specified, the Operator runs with its default arguments, namely `u=u, x_m=0, x_M=2, y_m=0, y_M=2, time_m=0, time_M=1`

.

At this point, the same Operator can be used for a completely different run, for example

```
>>> u2 = TimeFunction(name='u', grid=grid, save=5)
>>> summary = op.apply(u=u2, x_m=1, y_M=1)
```

Now, the Operator will run with a different set of arguments, namely `u=u2, x_m=1, x_M=2, y_m=0, y_M=1, time_m=0, time_M=3`

.

To run an Operator that only uses buffered TimeFunctions, the maximum iteration point along the time dimension must be explicitly specified (otherwise, the Operator wouldn’t know how many iterations to run).

```
>>> u3 = TimeFunction(name='u', grid=grid)
>>> op = Operator(Eq(u3.forward, u3 + 1))
>>> summary = op.apply(time_M=10)
```

### arguments

`arguments(**kwargs)`

Arguments to run the Operator.

### cinterface

`cinterface(force=False)`

Generate two files under the prescribed temporary directory:

```
* `X.c` (or `X.cpp`): the code generated for this Operator;
* `X.h`: an header file representing the interface of `X.c`.
```

Where `X=self.name`

.

#### Parameters

Name | Type | Description | Default |
---|---|---|---|

`force` |
bool | Overwrite any existing files. Defaults to False. | `False` |