Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions arraycontext/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -299,9 +299,19 @@ def zeros(self,
pass

def empty_like(self, ary: Array) -> Array:
from warnings import warn
warn(f"{type(self).__name__}.empty_like is deprecated and will stop "
"working in 2023. Prefer actx.np.zeros_like instead.",
DeprecationWarning, stacklevel=2)

return self.empty(shape=ary.shape, dtype=ary.dtype)

def zeros_like(self, ary: Array) -> Array:
from warnings import warn
warn(f"{type(self).__name__}.zeros_like is deprecated and will stop "
"working in 2023. Use actx.np.zeros_like instead.",
DeprecationWarning, stacklevel=2)

return self.zeros(shape=ary.shape, dtype=ary.dtype)

@abstractmethod
Expand Down
6 changes: 0 additions & 6 deletions arraycontext/fake_numpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,6 @@ def _get_fake_numpy_linalg_namespace(self):
# "interp",
})

def empty_like(self, ary):
return self._array_context.empty_like(ary)

def zeros_like(self, ary):
return self._array_context.zeros_like(ary)

def conjugate(self, x):
# NOTE: conjugate distributes over object arrays, but it looks for a
# `conjugate` ufunc, while some implementations only have the shorter
Expand Down
18 changes: 15 additions & 3 deletions arraycontext/impl/jax/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,11 @@ def _wrapper(ary):
# {{{ ArrayContext interface

def empty(self, shape, dtype):
from warnings import warn
warn(f"{type(self).__name__}.empty is deprecated and will stop "
"working in 2023. Prefer actx.zeros instead.",
DeprecationWarning, stacklevel=2)

import jax.numpy as jnp
return jnp.empty(shape=shape, dtype=dtype)

Expand All @@ -96,16 +101,23 @@ def zeros(self, shape, dtype):
return jnp.zeros(shape=shape, dtype=dtype)

def empty_like(self, ary):
from warnings import warn
warn(f"{type(self).__name__}.empty_like is deprecated and will stop "
"working in 2023. Prefer actx.np.zeros_like instead.",
DeprecationWarning, stacklevel=2)

def _empty_like(array):
return self.empty(array.shape, array.dtype)

return self._rec_map_container(_empty_like, ary)

def zeros_like(self, ary):
def _zeros_like(array):
return self.zeros(array.shape, array.dtype)
from warnings import warn
warn(f"{type(self).__name__}.zeros_like is deprecated and will stop "
"working in 2023. Use actx.np.zeros_like instead.",
DeprecationWarning, stacklevel=2)

return self._rec_map_container(_zeros_like, ary, default_scalar=0)
return self.np.zeros_like(ary)

def from_numpy(self, array):
def _from_numpy(ary):
Expand Down
19 changes: 19 additions & 0 deletions arraycontext/impl/jax/fake_numpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,25 @@ def __getattr__(self, name):

# {{{ array creation routines

def empty_like(self, ary):
from warnings import warn
warn(f"{type(self._array_context).__name__}.np.empty_like is "
"deprecated and will stop working in 2023. Prefer actx.np.zeros_like "
"instead.",
DeprecationWarning, stacklevel=2)

def _empty_like(array):
return self._array_context.empty(array.shape, array.dtype)

return self._array_context._rec_map_container(_empty_like, ary)

def zeros_like(self, ary):
def _zeros_like(array):
return self._array_context.zeros(array.shape, array.dtype)

return self._array_context._rec_map_container(
_zeros_like, ary, default_scalar=0)

def ones_like(self, ary):
return self.full_like(ary, 1)

Expand Down
21 changes: 15 additions & 6 deletions arraycontext/impl/pyopencl/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,11 @@ def _wrapper(ary):
# {{{ ArrayContext interface

def empty(self, shape, dtype):
from warnings import warn
warn(f"{type(self).__name__}.empty is deprecated and will stop "
"working in 2023. Prefer actx.zeros instead.",
DeprecationWarning, stacklevel=2)

import arraycontext.impl.pyopencl.taggable_cl_array as tga
return tga.empty(self.queue, shape, dtype, allocator=self.allocator)

Expand All @@ -197,6 +202,11 @@ def zeros(self, shape, dtype):
return tga.zeros(self.queue, shape, dtype, allocator=self.allocator)

def empty_like(self, ary):
from warnings import warn
warn(f"{type(self).__name__}.empty_like is deprecated and will stop "
"working in 2023. Prefer actx.np.zeros_like instead.",
DeprecationWarning, stacklevel=2)

import arraycontext.impl.pyopencl.taggable_cl_array as tga

def _empty_like(array):
Expand All @@ -206,13 +216,12 @@ def _empty_like(array):
return self._rec_map_container(_empty_like, ary)

def zeros_like(self, ary):
import arraycontext.impl.pyopencl.taggable_cl_array as tga

def _zeros_like(array):
return tga.zeros(self.queue, array.shape, array.dtype,
allocator=self.allocator, axes=array.axes, tags=array.tags)
from warnings import warn
warn(f"{type(self).__name__}.zeros_like is deprecated and will stop "
"working in 2023. Use actx.np.zeros_like instead.",
DeprecationWarning, stacklevel=2)

return self._rec_map_container(_zeros_like, ary, default_scalar=0)
return self.np.zeros_like(ary)

def from_numpy(self, array):
import arraycontext.impl.pyopencl.taggable_cl_array as tga
Expand Down
37 changes: 32 additions & 5 deletions arraycontext/impl/pyopencl/fake_numpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,22 +63,49 @@ def _get_fake_numpy_linalg_namespace(self):

# {{{ array creation routines

def empty_like(self, ary):
from warnings import warn
warn(f"{type(self._array_context).__name__}.np.empty_like is "
"deprecated and will stop working in 2023. Prefer actx.np.zeros_like "
"instead.",
DeprecationWarning, stacklevel=2)

import arraycontext.impl.pyopencl.taggable_cl_array as tga
actx = self._array_context

def _empty_like(array):
return tga.empty(actx.queue, array.shape, array.dtype,
allocator=actx.allocator, axes=array.axes, tags=array.tags)

return actx._rec_map_container(_empty_like, ary)

def zeros_like(self, ary):
import arraycontext.impl.pyopencl.taggable_cl_array as tga
actx = self._array_context

def _zeros_like(array):
return tga.zeros(
actx.queue, array.shape, array.dtype,
allocator=actx.allocator, axes=array.axes, tags=array.tags)

return actx._rec_map_container(_zeros_like, ary, default_scalar=0)

def ones_like(self, ary):
return self.full_like(ary, 1)

def full_like(self, ary, fill_value):
import arraycontext.impl.pyopencl.taggable_cl_array as tga
actx = self._array_context

def _full_like(subary):
filled = tga.empty(
self._array_context.queue, subary.shape, subary.dtype,
allocator=self._array_context.allocator,
axes=subary.axes, tags=subary.tags)
actx.queue, subary.shape, subary.dtype,
allocator=actx.allocator, axes=subary.axes, tags=subary.tags)
filled.fill(fill_value)

return filled

return self._array_context._rec_map_container(
_full_like, ary, default_scalar=fill_value)
return actx._rec_map_container(_full_like, ary, default_scalar=fill_value)

def copy(self, ary):
def _copy(subary):
Expand Down
16 changes: 10 additions & 6 deletions arraycontext/impl/pytato/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -348,10 +348,12 @@ def _wrapper(ary):
# {{{ ArrayContext interface

def zeros_like(self, ary):
def _zeros_like(array):
return self.zeros(array.shape, array.dtype)
from warnings import warn
warn(f"{type(self).__name__}.zeros_like is deprecated and will stop "
"working in 2023. Use actx.np.zeros_like instead.",
DeprecationWarning, stacklevel=2)

return self._rec_map_container(_zeros_like, ary, default_scalar=0)
return self.np.zeros_like(ary)

def from_numpy(self, array):
import pytato as pt
Expand Down Expand Up @@ -720,10 +722,12 @@ def _wrapper(ary):
# {{{ ArrayContext interface

def zeros_like(self, ary):
def _zeros_like(array):
return self.zeros(array.shape, array.dtype)
from warnings import warn
warn(f"{type(self).__name__}.zeros_like is deprecated and will stop "
"working in 2023. Use actx.np.zeros_like instead.",
DeprecationWarning, stacklevel=2)

return self._rec_map_container(_zeros_like, ary, default_scalar=0)
return self.np.zeros_like(ary)

def from_numpy(self, array):
import jax
Expand Down
7 changes: 7 additions & 0 deletions arraycontext/impl/pytato/fake_numpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,13 @@ def __getattr__(self, name):

# {{{ array creation routines

def zeros_like(self, ary):
def _zeros_like(array):
return self._array_context.zeros(array.shape, array.dtype)

return self._array_context._rec_map_container(
_zeros_like, ary, default_scalar=0)

def ones_like(self, ary):
return self.full_like(ary, 1)

Expand Down
33 changes: 33 additions & 0 deletions doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,39 @@ implementations for:
:mod:`arraycontext` started life as an array abstraction for use with the
:mod:`meshmode` unstrucuted discretization package.

Design Guidelines
-----------------

Here are some of the guidelines we aim to follow in :mod:`arraycontext`. There
exist numerous other, related efforts, such as the `Python array API standard
<https://data-apis.org/array-api/latest/purpose_and_scope.html>`__. These
points may aid in clarifying and differentiating our objectives.

- The array context is about exposing the common subset of operations
available in immutable and mutable arrays. As a result, the interface
does *not* seek to support interfaces that provide, enable, or are typically
used only with in-place mutation.

For example: The equivalents of :func:`numpy.empty` were deprecated
and will eventually be removed.

- Each array context offers a specific subset of of :mod:`numpy` under
:attr:`arraycontext.ArrayContext.np`. Functions under this namespace
must be unconditionally :mod:`numpy`-compatible, that is, they may not
offer an interface beyond what numpy offers. Functions that are
incompatible, for example by supporting tag metadata
(cf. :meth:`arraycontext.ArrayContext.einsum`) should live under the
:class:`~arraycontext.ArrayContext` directly.
Comment thread
kaushikcfd marked this conversation as resolved.
Resolved
Comment thread
alexfikl marked this conversation as resolved.
Resolved

- Similarly, we strive to minimize redundancy between attributes of
:class:`~arraycontext.ArrayContext` and :attr:`arraycontext.ArrayContext.np`.

For example: ``ArrayContext.empty_like`` was deprecated.

- Array containers are data structures that may contain arrays.
See :mod:`arraycontext.container`. We strive to support these, where sensible,
in :class:`~arraycontext.ArrayContext` and :attr:`arraycontext.ArrayContext.np`.

Contents
--------

Expand Down
30 changes: 13 additions & 17 deletions test/test_arraycontext.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
PytatoPyOpenCLArrayContext,
EagerJAXArrayContext,
ArrayContainer,
to_numpy, tag_axes)
tag_axes)
from arraycontext import ( # noqa: F401
pytest_generate_tests_for_array_contexts,
)
Expand Down Expand Up @@ -1145,23 +1145,22 @@ def test_numpy_conversion(actx_factory):
enthalpy=np.array(np.random.rand()),
)

from arraycontext import from_numpy, to_numpy
ac_actx = from_numpy(ac, actx)
ac_roundtrip = to_numpy(ac_actx, actx)
ac_actx = actx.from_numpy(ac)
ac_roundtrip = actx.to_numpy(ac_actx)

assert np.allclose(ac.mass, ac_roundtrip.mass)
assert np.allclose(ac.momentum[0], ac_roundtrip.momentum[0])

from dataclasses import replace
ac_with_cl = replace(ac, enthalpy=ac_actx.mass)
with pytest.raises(TypeError):
from_numpy(ac_with_cl, actx)
actx.from_numpy(ac_with_cl)

with pytest.raises(TypeError):
from_numpy(ac_actx, actx)
actx.from_numpy(ac_actx)

with pytest.raises(TypeError):
to_numpy(ac, actx)
actx.to_numpy(ac)

# }}}

Expand Down Expand Up @@ -1226,55 +1225,52 @@ def scale_and_orthogonalize(alpha, vel):


def test_actx_compile(actx_factory):
from arraycontext import (to_numpy, from_numpy)
actx = actx_factory()

compiled_rhs = actx.compile(scale_and_orthogonalize)

v_x = np.random.rand(10)
v_y = np.random.rand(10)

vel = from_numpy(Velocity2D(v_x, v_y, actx), actx)
vel = actx.from_numpy(Velocity2D(v_x, v_y, actx))

scaled_speed = compiled_rhs(np.float64(3.14), vel)

result = to_numpy(scaled_speed, actx)
result = actx.to_numpy(scaled_speed)
np.testing.assert_allclose(result.u, -3.14*v_y)
np.testing.assert_allclose(result.v, 3.14*v_x)


def test_actx_compile_python_scalar(actx_factory):
from arraycontext import (to_numpy, from_numpy)
actx = actx_factory()

compiled_rhs = actx.compile(scale_and_orthogonalize)

v_x = np.random.rand(10)
v_y = np.random.rand(10)

vel = from_numpy(Velocity2D(v_x, v_y, actx), actx)
vel = actx.from_numpy(Velocity2D(v_x, v_y, actx))

scaled_speed = compiled_rhs(3.14, vel)

result = to_numpy(scaled_speed, actx)
result = actx.to_numpy(scaled_speed)
np.testing.assert_allclose(result.u, -3.14*v_y)
np.testing.assert_allclose(result.v, 3.14*v_x)


def test_actx_compile_kwargs(actx_factory):
from arraycontext import (to_numpy, from_numpy)
actx = actx_factory()

compiled_rhs = actx.compile(scale_and_orthogonalize)

v_x = np.random.rand(10)
v_y = np.random.rand(10)

vel = from_numpy(Velocity2D(v_x, v_y, actx), actx)
vel = actx.from_numpy(Velocity2D(v_x, v_y, actx))

scaled_speed = compiled_rhs(3.14, vel=vel)

result = to_numpy(scaled_speed, actx)
result = actx.to_numpy(scaled_speed)
np.testing.assert_allclose(result.u, -3.14*v_y)
np.testing.assert_allclose(result.v, 3.14*v_x)

Expand Down Expand Up @@ -1550,7 +1546,7 @@ def test_to_numpy_on_frozen_arrays(actx_factory):
actx = actx_factory()
u = actx.freeze(actx.zeros(10, dtype="float64")+1)
np.testing.assert_allclose(actx.to_numpy(u), 1)
np.testing.assert_allclose(to_numpy(u, actx), 1)
np.testing.assert_allclose(actx.to_numpy(u), 1)


def test_tagging(actx_factory):
Expand Down