-
Notifications
You must be signed in to change notification settings - Fork 49
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
Functional: Index fields C++ backend support #1130
Conversation
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.
only minor problems with applying the new import style.
@@ -19,6 +19,7 @@ | |||
|
|||
import numpy as np | |||
|
|||
import functional.otf.binding.type_specifications as ts |
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.
I am all for using the same abbreviations for the same module. This however is using an established abbreviation for a different module (even though that module re-imports a lot of the usual one).
Note, once we accept that we shouldn't try to get away with using the same abbreviation for different modules, the reason for re-importing inside the local type_specifications
module goes away.
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.
Well, the idea was that the derived modules are a strict superset of the original type_system.*
modules, and in that case you can just simply import the extended module.
Sounds like it would be kinda nice to use, but to be honest it might be better as you suggest. Re-importing all the stuff is ugly and error-prone, and then not knowing which flavour of the module you are using at the moment is also a problem. Plus, it's really nasty if you want to use two extended modules at the same time.
Anyways, I think this is out of the scope of this PR, but we can change it in a follow-up PR.
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.
I agree we should not use the same alias for different modules. I think it would be good enough for this PR to just change the alias to a similar but different one. For example:
import functional.otf.binding.type_specifications as bts
from functional.type_system.type_specifications import ( # noqa: F401 | ||
CallableType as CallableType, | ||
DataType as DataType, | ||
DeferredType as DeferredType, | ||
DimensionType as DimensionType, | ||
FieldType as FieldType, | ||
FunctionType as FunctionType, | ||
OffsetType as OffsetType, | ||
ScalarKind as ScalarKind, | ||
ScalarType as ScalarType, | ||
TupleType as TupleType, | ||
TypeSpec as TypeSpec, | ||
VoidType as VoidType, | ||
) |
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.
If we don't import this module elsewhere as ts
, which should be reserved for functional.type_system.type_specifications
, then there is no reason for re-importing these classes.
The whole reason for the import <module> [as <name>]
style over import <member> from <module>
is to be able to track (without IDE help) what is coming from where. Why go out of our way to negate that advantage?
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.
I also agree with @DropD here. I think it would be better if users of the type specs add the original ts
module plus additional imports from extension modules to bring new types in scope, instead of trying to expand the original type_specifications
module (which makes basically impossible to compose type extensions from different modules):
# This allows using extensions from different modules very easily
import functional.type_system.type_specifications as ts
import functional.otf.binding.type_specifications as bts
@@ -18,22 +18,37 @@ | |||
|
|||
import numpy as np | |||
|
|||
import functional.otf.binding.type_specifications as ts |
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.
ts
name should be reserved for functional.type_system.type_specifications
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.
Overall, it looks good to me. I left a few comments and suggestions to improve the PR.
else: | ||
raise ValueError("unsupported scalar type") |
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.
raise ValueError("unsupported scalar type") | |
raise ValueError(f"Unsupported '{scalar_type}' scalar type.") |
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.
I didn't invest too much into exceptions because I want to clean the whole thing up next cycle. Anyways, you have a point that even clean-up is easier if the messages are better to begin with, so I added some better messages.
return f"BufferT{index}&& {param.name}" | ||
else: | ||
raise ValueError("unsupported type for parameters") |
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.
raise ValueError("unsupported type for parameters") | |
raise ValueError(f"Unsupported '{param.type_}' type for parameters.") |
src/functional/otf/binding/pybind.py
Outdated
else: | ||
type_str = cpp_interface.render_python_type(param.dtype.type) | ||
return type_str + " " + param.name | ||
raise ValueError("Invalid type.") |
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.
raise ValueError("Invalid type.") | |
raise ValueError(f"Invalid type in '{param}' parameter.") |
# TODO: make type_translation extendable and specialize it for the | ||
# bindings to distinguish between buffer-backed fields and index | ||
# fields. |
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.
I now understand that the comment about making type_translation
extendable is related to use a different mechanism for the pattern matching part with the actual type, and not with the preliminary handling of the messy implementation details of python typing annotations. It makes sense, but still don't see it as a high priority for now.
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.
I wrote a new from_value
function that tries to match for IndexFieldType
and if it fails it delegates it to the original functional.type_system.from_value
function.
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.
I'd like to use a registry that allows you to register a match functor for every type of the type_system
, then you can extend without raising the cyclomatic complexity through all these if statements and you can also simply chain the list of matchers to combine modules.
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.
That would also replace the __gt_type__
solution which is an intrusive implementation because it forces objects to be aware of the type system.
if isinstance(obj, IndexField): | ||
dtype = type_translation.from_type_hint(obj.dtype.type) | ||
assert isinstance(dtype, ts.ScalarType) | ||
return interface.Parameter(name, ts.IndexFieldType(axis=obj.axis, dtype=dtype)) |
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.
I think that, for consistency, this piece of code should be moved into type_translation.from_value()
, since the similar handling of embedded.LocatedField
is also implemented there. Additionally, it's easier to refactor the type translation code in the future if it is not spread across different functions.
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.
I moved it to a new function from_value
under functional.otf.binding.type_translation
. It shouldn't be moved into the main type_translation
module because IndexFieldType
is not present there.
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.
Just adding the partial review I already started.
param: interface.ScalarParameter | interface.BufferParameter, index: int | ||
) -> str: | ||
if isinstance(param, interface.ScalarParameter): | ||
return f"{render_python_type(param.scalar_type.type)} {param.name}" |
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.
The render_python_type
function and _TYPE_MAPPING
dict is now dead code and should be removed right?
Extracted from #1130 and married with Connectivities. Co-authored-by: Enrique G. Paredes <18477+egparedes@users.noreply.github.com>
Adds index fields as a distinct argument type for the C++ binding generator. The binding generator generates the correct C++ code that passes
gridtools::stencil::positional
iterators to the GTFN C++ program code.