-
Notifications
You must be signed in to change notification settings - Fork 11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement CupyArrayContext #251
base: main
Are you sure you want to change the base?
Changes from all commits
7ab5211
96b7a3d
8dee38d
2c025eb
d6e3136
bfa648a
27e5a19
6d507e1
8fb4e0b
be70b67
677419b
6250211
d61f0cf
5871ae7
9c56443
fd95813
5f4c4d9
a8fe272
2296c6d
8fd5488
6f3cd94
ab8266d
79b0bc3
70aff99
8b5c6cf
8561b2f
904e061
340f9dc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -40,6 +40,10 @@ jobs: | |
run: | | ||
USE_CONDA_BUILD=1 | ||
curl -L -O https://gitlab.tiker.net/inducer/ci-support/raw/master/prepare-and-run-pylint.sh | ||
|
||
CONDA_ENVIRONMENT=.test-conda-env-py3.yml | ||
echo "- cupy" >> "$CONDA_ENVIRONMENT" | ||
|
||
. ./prepare-and-run-pylint.sh "$(basename $GITHUB_REPOSITORY)" examples/*.py test/test_*.py | ||
|
||
mypy: | ||
|
@@ -52,6 +56,9 @@ jobs: | |
curl -L -O https://tiker.net/ci-support-v0 | ||
. ./ci-support-v0 | ||
|
||
CONDA_ENVIRONMENT=.test-conda-env-py3.yml | ||
echo "- cupy" >> "$CONDA_ENVIRONMENT" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems that CI for this is not running on Github (and cannot run). Why install the package then? (Also above.) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If cupy is not installed, these tests will fail, e.g. mypy with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you adapt There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done in 79b0bc3 et al., and seems to work: https://gitlab.tiker.net/inducer/arraycontext/-/jobs/804938 |
||
|
||
build_py_project_in_conda_env | ||
python -m pip install mypy pytest | ||
./run-mypy.sh | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,6 +16,7 @@ code to work with all of them? No problem! Comes with pre-made array context | |
implementations for: | ||
|
||
- numpy | ||
- cupy | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not make this into a link to cupy.dev or something? |
||
- `PyOpenCL <https://documen.tician.de/pyopencl/array.html>`__ | ||
- `JAX <https://jax.readthedocs.io/en/latest/>`__ | ||
- `Pytato <https://documen.tician.de/pytato>`__ (for lazy/deferred evaluation) | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,207 @@ | ||||||
""" | ||||||
.. currentmodule:: arraycontext | ||||||
|
||||||
A :mod:`cupy`-based array context. | ||||||
|
||||||
.. autoclass:: CupyArrayContext | ||||||
""" | ||||||
|
||||||
from __future__ import annotations | ||||||
|
||||||
|
||||||
__copyright__ = """ | ||||||
Copyright (C) 2024 University of Illinois Board of Trustees | ||||||
""" | ||||||
|
||||||
__license__ = """ | ||||||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
of this software and associated documentation files (the "Software"), to deal | ||||||
in the Software without restriction, including without limitation the rights | ||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||||
copies of the Software, and to permit persons to whom the Software is | ||||||
furnished to do so, subject to the following conditions: | ||||||
|
||||||
The above copyright notice and this permission notice shall be included in | ||||||
all copies or substantial portions of the Software. | ||||||
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||||
THE SOFTWARE. | ||||||
""" | ||||||
|
||||||
from typing import Any, overload | ||||||
|
||||||
import numpy as np | ||||||
|
||||||
import loopy as lp | ||||||
from pytools.tag import ToTagSetConvertible | ||||||
|
||||||
from arraycontext.container.traversal import rec_map_array_container, with_array_context | ||||||
from arraycontext.context import ( | ||||||
Array, | ||||||
ArrayContext, | ||||||
ArrayOrContainerOrScalar, | ||||||
ArrayOrContainerOrScalarT, | ||||||
ContainerOrScalarT, | ||||||
NumpyOrContainerOrScalar, | ||||||
UntransformedCodeWarning, | ||||||
) | ||||||
|
||||||
|
||||||
class CupyNonObjectArrayMetaclass(type): | ||||||
def __instancecheck__(cls, instance: Any) -> bool: | ||||||
import cupy as cp # type: ignore[import-untyped] | ||||||
return isinstance(instance, cp.ndarray) and instance.dtype != object | ||||||
|
||||||
|
||||||
class CupyNonObjectArray(metaclass=CupyNonObjectArrayMetaclass): | ||||||
pass | ||||||
|
||||||
|
||||||
class CupyArrayContext(ArrayContext): | ||||||
""" | ||||||
An :class:`ArrayContext` that uses :class:`cupy.ndarray` to represent arrays. | ||||||
|
||||||
.. automethod:: __init__ | ||||||
""" | ||||||
|
||||||
_loopy_transform_cache: dict[lp.TranslationUnit, lp.ExecutorBase] | ||||||
|
||||||
def __init__(self, device: int | None = None) -> None: | ||||||
super().__init__() | ||||||
self._loopy_transform_cache = {} | ||||||
|
||||||
if device is not None: | ||||||
import cupy as cp | ||||||
cp.cuda.runtime.setDevice(device) | ||||||
Comment on lines
+78
to
+80
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How do devices work in
|
||||||
|
||||||
array_types = (CupyNonObjectArray,) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Put this before |
||||||
|
||||||
def _get_fake_numpy_namespace(self): | ||||||
from .fake_numpy import CupyFakeNumpyNamespace | ||||||
return CupyFakeNumpyNamespace(self) | ||||||
|
||||||
# {{{ ArrayContext interface | ||||||
|
||||||
def clone(self): | ||||||
return type(self)() | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
If the device setting sticks around. |
||||||
|
||||||
@overload | ||||||
def from_numpy(self, array: np.ndarray) -> Array: | ||||||
Comment on lines
+93
to
+94
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this necessary here as well? I don't think any of the other implementations have it and |
||||||
... | ||||||
|
||||||
@overload | ||||||
def from_numpy(self, array: ContainerOrScalarT) -> ContainerOrScalarT: | ||||||
... | ||||||
|
||||||
def from_numpy(self, | ||||||
array: NumpyOrContainerOrScalar | ||||||
) -> ArrayOrContainerOrScalar: | ||||||
import cupy as cp | ||||||
|
||||||
def _from_numpy(ary): | ||||||
return cp.array(ary) | ||||||
|
||||||
return with_array_context(rec_map_array_container(_from_numpy, array), | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
? Doesn't seem useful to wrap it if there are no additional arguments. |
||||||
actx=self) | ||||||
|
||||||
@overload | ||||||
def to_numpy(self, array: Array) -> np.ndarray: | ||||||
... | ||||||
|
||||||
@overload | ||||||
def to_numpy(self, array: ContainerOrScalarT) -> ContainerOrScalarT: | ||||||
... | ||||||
|
||||||
def to_numpy(self, | ||||||
array: ArrayOrContainerOrScalar | ||||||
) -> NumpyOrContainerOrScalar: | ||||||
import cupy as cp | ||||||
|
||||||
def _to_numpy(ary): | ||||||
return cp.asnumpy(ary) | ||||||
|
||||||
return with_array_context(rec_map_array_container(_to_numpy, array), | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
actx=None) | ||||||
|
||||||
def call_loopy( | ||||||
self, | ||||||
t_unit: lp.TranslationUnit, **kwargs: Any | ||||||
) -> dict[str, Array]: | ||||||
t_unit = t_unit.copy(target=lp.ExecutableCTarget()) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this supposed to work? i.e. do the |
||||||
try: | ||||||
executor = self._loopy_transform_cache[t_unit] | ||||||
except KeyError: | ||||||
executor = self.transform_loopy_program(t_unit).executor() | ||||||
self._loopy_transform_cache[t_unit] = executor | ||||||
|
||||||
_, result = executor(**kwargs) | ||||||
|
||||||
return result | ||||||
|
||||||
def freeze(self, array): | ||||||
import cupy as cp | ||||||
|
||||||
def _freeze(ary): | ||||||
return cp.asnumpy(ary) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does |
||||||
|
||||||
return with_array_context(rec_map_array_container(_freeze, array), actx=None) | ||||||
|
||||||
def thaw(self, array): | ||||||
import cupy as cp | ||||||
|
||||||
def _thaw(ary): | ||||||
return cp.array(ary) | ||||||
|
||||||
return with_array_context(rec_map_array_container(_thaw, array), actx=self) | ||||||
|
||||||
# }}} | ||||||
|
||||||
def transform_loopy_program(self, t_unit): | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be deleted. |
||||||
from warnings import warn | ||||||
warn("Using the base " | ||||||
f"{type(self).__name__}.transform_loopy_program " | ||||||
"to transform a translation unit. " | ||||||
"This is a no-op and will result in unoptimized C code for" | ||||||
"the requested optimization, all in a single statement." | ||||||
"This will work, but is unlikely to be performant." | ||||||
f"Instead, subclass {type(self).__name__} and implement " | ||||||
"the specific transform logic required to transform the program " | ||||||
"for your package or application. Check higher-level packages " | ||||||
"(e.g. meshmode), which may already have subclasses you may want " | ||||||
"to build on.", | ||||||
UntransformedCodeWarning, stacklevel=2) | ||||||
|
||||||
return t_unit | ||||||
|
||||||
def tag(self, | ||||||
tags: ToTagSetConvertible, | ||||||
array: ArrayOrContainerOrScalarT) -> ArrayOrContainerOrScalarT: | ||||||
# Cupy (like numpy) doesn't support tagging | ||||||
return array | ||||||
|
||||||
def tag_axis(self, | ||||||
iaxis: int, tags: ToTagSetConvertible, | ||||||
array: ArrayOrContainerOrScalarT) -> ArrayOrContainerOrScalarT: | ||||||
# Cupy (like numpy) doesn't support tagging | ||||||
return array | ||||||
|
||||||
def einsum(self, spec, *args, arg_names=None, tagged=()): | ||||||
import cupy as cp | ||||||
return cp.einsum(spec, *args) | ||||||
|
||||||
@property | ||||||
def permits_inplace_modification(self): | ||||||
return True | ||||||
|
||||||
@property | ||||||
def supports_nonscalar_broadcasting(self): | ||||||
return True | ||||||
|
||||||
@property | ||||||
def permits_advanced_indexing(self): | ||||||
return True |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These probably also need to be added to
.gitlab-ci.yml