Skip to content
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

Add interface to check whether in a DaCe parsing context #998

Merged
merged 2 commits into from
May 23, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 12 additions & 6 deletions dace/frontend/python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,27 @@ also address any questions you have to alziogas@inf.ethz.ch

## Supported Python Versions

The DaCe framework officially supports Python 3 up to version 3.7.
The Python-Frontend also works with version 3.8. However, the module SymPy
The DaCe framework officially supports Python 3 from version 3.7.
The Python-Frontend also works with version 3.8-3.10. However, the module SymPy
must be updated to version 1.6.2 or newer. Please note that there are some
issues between DaCe and SymPy 1.6.2 (see [#367](https://github.com/spcl/dace/pull/367)).

**Neither the DaCe framework nor the Python-Frontend have been tested with
Python version 3.9**

## Main Limitations

- Classes are not supported.
- Classes are only supported in JIT mode.
- Lists, sets, and dictionaries are not supported as data. There is limited support for other uses, e.g., as arguments to some methods.
- Only `range`, `parrange`, and `dace.map` iterators are supported.
- Recursion is not supported.

## Automatic parsing

By default, DaCe tries to parse every call as a dace.program. If the object being called has an `__sdfg__` method, it will
be used instead of trying to parse `__call__`. Additionally, a function called `dace.in_program()` returns `True` while in a
DaCe parsing context.

If parsing fails, DaCe will try to automatically generate a callback to the Python interpreter, marshalling types such
as NumPy/CuPy arrays such that they match internal data containers. A warning will also be raised when this happens.

## NumPy Compatibility

The Python-Frontend currently supports a limited subset of NumPy:
Expand Down
17 changes: 14 additions & 3 deletions dace/frontend/python/interface.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
# Copyright 2019-2021 ETH Zurich and the DaCe authors. All rights reserved.
""" Python interface for DaCe functions. """

from functools import wraps
import inspect
from functools import wraps
from typing import Any, Callable, Deque, Dict, Generator, Optional, Tuple, TypeVar, Union, overload

from dace import dtypes
from dace.dtypes import paramdec
from dace.frontend.python import parser, ndloop, tasklet_runner
from typing import (Any, Callable, Deque, Dict, Generator, Optional, Tuple, TypeVar, overload, Union)
from dace.frontend.python import ndloop, parser, tasklet_runner

#############################################

Expand Down Expand Up @@ -226,9 +227,19 @@ def unroll(generator):
"""
yield from generator


def nounroll(generator):
"""
Explicitly annotates that a loop should not be unrolled during parsing.
:param generator: The original generator to loop over.
"""
yield from generator


def in_program() -> bool:
"""
Returns True if in a DaCe program parsing context. This function can be used to test whether the current
code runs inside the ``@dace.program`` parser.
:return: True if in a DaCe program parsing context, or False otherwise.
"""
return False
3 changes: 2 additions & 1 deletion dace/frontend/python/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def _get_locals_and_globals(f):
""" Retrieves a list of local and global variables for the function ``f``.
This is used to retrieve variables around and defined before @dace.programs for adding symbols and constants.
"""
result = {}
result = {'__dace__': True}
# Update globals, then locals
result.update(f.__globals__)
# grab the free variables (i.e. locals)
Expand Down Expand Up @@ -77,6 +77,7 @@ def infer_symbols_from_datadescriptor(sdfg: SDFG, args: Dict[str, Any],
exclude = set(symbolic.symbol(s) for s in exclude)
equations = []
symbols = set()

# Collect equations and symbols from arguments and shapes
for arg_name, arg_val in args.items():
if arg_name in sdfg.arrays:
Expand Down
10 changes: 10 additions & 0 deletions dace/frontend/python/preprocessing.py
Original file line number Diff line number Diff line change
Expand Up @@ -579,12 +579,22 @@ def visit_Subscript(self, node: ast.Subscript) -> Any:
return self.visit_Attribute(node)

def visit_Call(self, node: ast.Call) -> Any:
from dace.frontend.python.interface import in_program # Avoid import loop

if hasattr(node.func, 'n') and isinstance(node.func.n, SDFGConvertible):
# Skip already-parsed calls
return self.generic_visit(node)

try:
global_func = astutils.evalnode(node.func, self.globals)

# Built-in functions are resolved directly
if global_func is in_program:
return self.global_value_to_node(True,
parent_node=node,
qualname=astutils.unparse(node),
recurse=True)

if self.resolve_functions:
global_val = astutils.evalnode(node, self.globals)
else:
Expand Down
24 changes: 24 additions & 0 deletions tests/python_frontend/parsing_context_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Copyright 2019-2022 ETH Zurich and the DaCe authors. All rights reserved.
import dace
import numpy as np


def test_parsing_context():
def func(a):
if dace.in_program():
a[:] = 1
else:
a[:] = 2

first = np.random.rand(10)
second = np.random.rand(10)

func(first)
dace.program(func)(second)

assert np.allclose(first, 2)
assert np.allclose(second, 1)


if __name__ == '__main__':
test_parsing_context()