Array Containers#

class arraycontext.ArrayContainer(*args, **kwargs)[source]#

A protocol for generic containers of the array type supported by the ArrayContext.

The functionality required for the container to operated is supplied via functools.singledispatch(). Implementations of the following functions need to be registered for a type serving as an ArrayContainer:

This allows enumeration of the component arrays in a container and the construction of modified containers from an iterable of those component arrays.

Packages may register their own types as array containers. They must not register other types (e.g. list) as array containers. The type numpy.ndarray is considered an array container, but only arrays with dtype object may be used as such. (This is so because object arrays cannot be distinguished from non-object arrays via their type.)

The container and its serialization interface has goals and uses approaches similar to JAX’s PyTrees, however its implementation differs a bit.

Note

This class is used in type annotation and as a marker of array container attributes for dataclass_array_container(). As a protocol, it is not intended as a superclass.

class arraycontext.ArrayContainerT#

A type variable with a lower bound of ArrayContainer.

exception arraycontext.NotAnArrayContainerError[source]#

TypeError subclass raised when an array container is expected.

Serialization/deserialization#

arraycontext.is_array_container_type(cls: type) bool[source]#
Returns:

True if the type cls has a registered implementation of serialize_container(), or if it is an ArrayContainer.

Warning

Not all instances of a type that this function labels an array container must automatically be array containers. For example, while this function will say that numpy.ndarray is an array container type, only object arrays actually are array containers.

arraycontext.serialize_container(ary: ArrayContainer) Iterable[Tuple[Any, ArrayOrContainer]][source]#
arraycontext.serialize_container(ary: numpy.ndarray) Iterable[Tuple[Any, ArrayOrContainer]]

Serialize the array container into an iterable over its components.

The order of the components and their identifiers are entirely under the control of the container class. However, the order is required to be deterministic, i.e. two calls to serialize_container() on array containers of the same types with the same number of sub-arrays must result in an iterable with the keys in the same order.

If ary is mutable, the serialization function is not required to ensure that the serialization result reflects the array state at the time of the call to serialize_container().

Returns:

an Iterable of 2-tuples where the first entry is an identifier for the component and the second entry is an array-like component of the ArrayContainer. Components can themselves be ArrayContainers, allowing for arbitrarily nested structures. The identifiers need to be hashable but are otherwise treated as opaque.

arraycontext.deserialize_container(template: ArrayContainerT, iterable: Iterable[Tuple[Any, Any]]) ArrayContainerT[source]#
arraycontext.deserialize_container(template: numpy.ndarray, iterable: Iterable[Tuple[Any, ArrayOrContainer]]) numpy.ndarray

Deserialize an iterable into an array container.

Parameters:
  • template – an instance of an existing object that can be used to aid in the deserialization. For a similar choice see __array_finalize__.

  • iterable – an iterable that mirrors the output of serialize_container().

Context retrieval#

arraycontext.get_container_context_opt(ary: ArrayContainer) ArrayContext | None[source]#

Retrieves the ArrayContext from the container, if any.

This function is not recursive, so it will only search at the root level of the container. For the recursive version, see get_container_context_recursively().

arraycontext.get_container_context_recursively(ary: ArrayContainer) ArrayContext | None[source]#

Walks the ArrayContainer hierarchy to find an ArrayContext associated with it.

If different components that have different array contexts are found at any level, an assertion error is raised.

Raises an error if no array container is found.

arraycontext.get_container_context_recursively_opt(ary: ArrayContainer) ArrayContext | None[source]#

Walks the ArrayContainer hierarchy to find an ArrayContext associated with it.

If different components that have different array contexts are found at any level, an assertion error is raised.

Returns None if no array context was found.

MultiVector support#

arraycontext.register_multivector_as_array_container() None[source]#

Registers MultiVector as an ArrayContainer. This function may be called multiple times. The second and subsequent calls have no effect.

Canonical locations for type annotations#

class arraycontext.container.ArrayContainerT#
Canonical:

arraycontext.ArrayContainerT

class arraycontext.container.ArrayOrContainerT#
Canonical:

arraycontext.ArrayOrContainerT

Containers with arithmetic#

arraycontext.with_container_arithmetic(*, bcast_number: bool = True, _bcast_actx_array_type: bool | None = None, bcast_obj_array: bool | None = None, bcast_numpy_array: bool = False, bcast_container_types: Tuple[type, ...] | None = None, arithmetic: bool = True, matmul: bool = False, bitwise: bool = False, shift: bool = False, _cls_has_array_context_attr: bool | None = None, eq_comparison: bool | None = None, rel_comparison: bool | None = None) Callable[[type], type][source]#

A class decorator that implements built-in operators for array containers by propagating the operations to the elements of the container.

Parameters:
  • bcast_number – If True, numbers broadcast over the container (with the container as the ‘outer’ structure).

  • _bcast_actx_array_type – If True, instances of base array types of the container’s array context are broadcasted over the container. Can be True only if the container has _cls_has_array_context_attr set. Defaulted to bcast_number if _cls_has_array_context_attr is set, else False.

  • bcast_obj_array – If True, numpy object arrays broadcast over the container. (with the container as the ‘inner’ structure)

  • bcast_numpy_array – If True, any numpy.ndarray will broadcast over the container. (with the container as the ‘inner’ structure) If this is set to True, bcast_obj_array must also be True.

  • bcast_container_types – A sequence of container types that will broadcast over this container (with this container as the ‘outer’ structure). numpy.ndarray is permitted to be part of this sequence to indicate that, in such broadcasting situations, this container should be the ‘outer’ structure. In this case, bcast_obj_array (and consequently bcast_numpy_array) must be False.

  • arithmetic – Implement the conventional arithmetic operators, including **, divmod(), and //. Also includes + and - as well as abs().

  • bitwise – If True, implement bitwise and, or, not, and inversion.

  • shift – If True, implement bit shifts.

  • eq_comparison – If True, implement == and !=.

  • rel_comparison – If True, implement <, <=, >, >=. In that case, if eq_comparison is unspecified, it is also set to True.

  • _cls_has_array_context_attr – A flag indicating whether the decorated class has an array_context attribute. If so, and if __debug__ is True, an additional check is performed in binary operators to ensure that both containers use the same array context. If None (the default), this value is set based on whether the class has an array_context attribute. Consider this argument an unstable interface. It may disappear at any moment.

Each operator class also includes the “reverse” operators if applicable.

Note

To generate the code implementing the operators, this function relies on class methods _deserialize_init_arrays_code and _serialize_init_arrays_code. This interface should be considered undocumented and subject to change, however if you are curious, you may look at its implementation in meshmode.dof_array.DOFArray. For a simple structure type, the implementation might look like this:

@classmethod
def _serialize_init_arrays_code(cls, instance_name):
    return {"u": f"{instance_name}.u", "v": f"{instance_name}.v"}

@classmethod
def _deserialize_init_arrays_code(cls, tmpl_instance_name, args):
    return f"u={args['u']}, v={args['v']}"

dataclass_array_container() automatically generates an appropriate implementation of these methods, so with_container_arithmetic() should nest “outside” :func:dataclass_array_container`.

Containers based on dataclasses#

arraycontext.dataclass_array_container(cls: type) type[source]#

A class decorator that makes the class to which it is applied an ArrayContainer by registering appropriate implementations of serialize_container() and deserialize_container(). cls must be a dataclass().

Attributes that are not array containers are allowed. In order to decide whether an attribute is an array container, the declared attribute type is checked by the criteria from is_array_container_type(). This includes some support for type annotations:

  • a typing.Union of array containers is considered an array container.

  • other type annotations, e.g. typing.Optional, are not considered array containers, even if they wrap one.

Traversing containers#

arraycontext.map_array_container(f: Callable[[Any], Any], ary: Array | ArrayContainer) Array | ArrayContainer[source]#

Applies f to all components of an ArrayContainer.

Works similarly to obj_array_vectorize(), but on arbitrary containers.

For a recursive version, see rec_map_array_container().

Parameters:

ary – a (potentially nested) structure of ArrayContainers, or an instance of a base array type.

arraycontext.multimap_array_container(f: Callable[[...], Any], *args: Any) Any[source]#

Applies f to the components of multiple ArrayContainers.

Works similarly to obj_array_vectorize_n_args(), but on arbitrary containers. The containers must all have the same type, which will also be the return type.

For a recursive version, see rec_multimap_array_container().

Parameters:

args – all ArrayContainer arguments must be of the same type and with the same structure (same number of components, etc.).

arraycontext.rec_map_array_container(f: Callable[[Any], Any], ary: Array | ArrayContainer, leaf_class: type | None = None) Array | ArrayContainer[source]#

Applies f recursively to an ArrayContainer.

For a non-recursive version see map_array_container().

Parameters:

ary – a (potentially nested) structure of ArrayContainers, or an instance of a base array type.

arraycontext.rec_multimap_array_container(f: Callable[[...], Any], *args: Any, leaf_class: type | None = None) Any[source]#

Applies f recursively to multiple ArrayContainers.

For a non-recursive version see multimap_array_container().

Parameters:

args – all ArrayContainer arguments must be of the same type and with the same structure (same number of components, etc.).

arraycontext.map_reduce_array_container(reduce_func: Callable[[Iterable[Any]], Any], map_func: Callable[[Any], Any], ary: ArrayOrContainerT) Array[source]#

Perform a map-reduce over array containers.

Parameters:
arraycontext.multimap_reduce_array_container(reduce_func: Callable[[Iterable[Any]], Any], map_func: Callable[[...], Any], *args: Any) Array | ArrayContainer[source]#

Perform a map-reduce over multiple array containers.

Parameters:
arraycontext.rec_map_reduce_array_container(reduce_func: Callable[[Iterable[Any]], Any], map_func: Callable[[Any], Any], ary: Array | ArrayContainer, leaf_class: type | None = None) Array | ArrayContainer[source]#

Perform a map-reduce over array containers recursively.

Parameters:
  • reduce_func – callable used to reduce over the components of ary (and those of its sub-containers) if ary is a ArrayContainer. Must be associative.

  • map_func – callable used to map a single array of type arraycontext.ArrayContext.array_types. Returns an array of the same type or a scalar.

Note

The traversal order is unspecified. reduce_func must be associative in order to guarantee a sensible result. This is because reduce_func may be called on subsets of the component arrays, and then again (potentially multiple times) on the results. As an example, consider a container made up of two sub-containers, subcontainer0 and subcontainer1, that each contain two component arrays, array0 and array1. The same result must be computed whether traversing recursively:

reduce_func([
    reduce_func([
        map_func(subcontainer0.array0),
        map_func(subcontainer0.array1)]),
    reduce_func([
        map_func(subcontainer1.array0),
        map_func(subcontainer1.array1)])])

reducing all of the arrays at once:

reduce_func([
    map_func(subcontainer0.array0),
    map_func(subcontainer0.array1),
    map_func(subcontainer1.array0),
    map_func(subcontainer1.array1)])

or any other such traversal.

arraycontext.rec_multimap_reduce_array_container(reduce_func: Callable[[Iterable[Any]], Any], map_func: Callable[[...], Any], *args: Any, leaf_class: type | None = None) Array | ArrayContainer[source]#

Perform a map-reduce over multiple array containers recursively.

Parameters:
  • reduce_func – callable used to reduce over the components of any ArrayContainers in *args (and those of their sub-containers). Must be associative.

  • map_func – callable used to map a single array of type arraycontext.ArrayContext.array_types. Returns an array of the same type or a scalar.

Note

The traversal order is unspecified. reduce_func must be associative in order to guarantee a sensible result. See rec_map_reduce_array_container() for additional details.

arraycontext.stringify_array_container_tree(ary: Array | ArrayContainer) str[source]#
Returns:

a string for an ASCII tree representation of the array container, similar to asciitree.

Traversing decorators#

arraycontext.mapped_over_array_containers(f: Callable[[Array | ArrayContainer], Array | ArrayContainer] | None = None, leaf_class: type | None = None) Callable[[Array | ArrayContainer], Array | ArrayContainer] | Callable[[Callable[[Any], Any]], Callable[[Array | ArrayContainer], Array | ArrayContainer]][source]#

Decorator around rec_map_array_container().

arraycontext.multimapped_over_array_containers(f: Callable[[...], Any] | None = None, leaf_class: type | None = None) Callable[[...], Any] | Callable[[Callable[[...], Any]], Callable[[...], Any]][source]#

Decorator around rec_multimap_array_container().

Freezing and thawing#

arraycontext.freeze(ary: ArrayOrContainerT, actx: ArrayContext | None = None) ArrayOrContainerT[source]#

Freezes recursively by going through all components of the ArrayContainer ary.

Parameters:

ary – a thaw()ed ArrayContainer.

Array container types may use functools.singledispatch() .register to register additional implementations.

See ArrayContext.thaw().

arraycontext.thaw(ary: ArrayOrContainerT, actx: ArrayContext) ArrayOrContainerT[source]#

Thaws recursively by going through all components of the ArrayContainer ary.

Parameters:

ary – a freeze()ed ArrayContainer.

Array container types may use functools.singledispatch() .register to register additional implementations.

See ArrayContext.thaw().

Serves as the registration point (using singledispatch() .register to register additional implementations for thaw().

Note

This function has the reverse argument order from the original function in meshmode. This was necessary because singledispatch() only dispatches on the first argument.

Flattening and unflattening#

arraycontext.flatten(ary: Array | ArrayContainer, actx: ArrayContext, *, leaf_class: type | None = None) Any[source]#

Convert all arrays in the ArrayContainer into single flat array of a type arraycontext.ArrayContext.array_types.

The operation requires arraycontext.ArrayContext.np to have ravel and concatenate methods implemented. The order in which the individual leaf arrays appear in the final array is dependent on the order given by serialize_container().

If leaf_class is given, then unflatten() will not be able to recover the original ary.

Parameters:

leaf_class – an ArrayContainer class on which the recursion is stopped (subclasses are not considered). If given, only the entries of this type are flattened and the rest of the tree structure is left as is. By default, the recursion is stopped when a non-ArrayContainer is found, which results in the whole input container ary being flattened.

arraycontext.unflatten(template: ArrayOrContainerT, ary: Array, actx: ArrayContext, *, strict: bool = True) ArrayOrContainerT[source]#

Unflatten an array ary produced by flatten() back into an ArrayContainer.

The order and sizes of each slice into ary are determined by the array container template.

Parameters:
  • ary – a flat one-dimensional array with a size that matches the number of entries in template.

  • strict – if True additional dtype and stride checking is performed on the unflattened array. Otherwise, these checks are skipped.

arraycontext.flat_size_and_dtype(ary: Array | ArrayContainer) Tuple[int, dtype[Any] | None][source]#
Returns:

a tuple (size, dtype) that would be the length and numpy.dtype of the one-dimensional array returned by flatten().

Numpy conversion#

arraycontext.from_numpy(ary: ndarray | int | float | complex | generic, actx: ArrayContext) Array | ArrayContainer | int | float | complex | generic[source]#

Convert all numpy arrays in the ArrayContainer to the base array type of ArrayContext.

The conversion is done using arraycontext.ArrayContext.from_numpy().

arraycontext.to_numpy(ary: Array | ArrayContainer, actx: ArrayContext) Array | ArrayContainer[source]#

Convert all arrays in the ArrayContainer to numpy using the provided ArrayContext actx.

The conversion is done using arraycontext.ArrayContext.to_numpy().

Algebraic operations#

arraycontext.outer(a: Any, b: Any) Any[source]#

Compute the outer product of a and b while allowing either of them to be an ArrayContainer.

Tweaks the behavior of numpy.outer() to return a lower-dimensional object if either/both of a and b are scalars (whereas numpy.outer() always returns a matrix). Here the definition of “scalar” includes all non-array-container types and any scalar-like array container types (including non-object numpy arrays).

If a and b are both array containers, the result will have the same type as a. If both are array containers and neither is an object array, they must have the same type.