Commit 9aa9da00 authored by Pierre Augier's avatar Pierre Augier
Browse files

TypeFormatterCython

parent 12ad63c62e3f
export func(float, float)
export func(float64, float64)
export func(float64[:, :], float64[:, :])
export func(int[:, :], float64[:, :])
export func1(int, float)
export func1(int, float64)
export __transonic__
......@@ -53,7 +53,7 @@ class Backend:
def __init__(self):
self.name = self.backend_name
self.name_capitalized = self.name.capitalize()
self.type_formatter = TypeFormatter(self.name)
self.type_formatter = self._TypeFormatter(self.name)
self.jit = self._SubBackendJIT(self.name, self.type_formatter)
def _make_code_from_fdef_node(self, fdef):
......
......@@ -117,6 +117,7 @@ class SubBackendJIT:
def compute_typename_from_object(self, obj: object):
"""return the Pythran type name"""
# TODO: use a (new) public function typeof
name = type(obj).__name__
name = self.type_formatter.normalize_type_name(name)
......@@ -140,6 +141,8 @@ class SubBackendJIT:
for key, value in obj.items():
break
# FIXME: we could check if the dict is homogeneous...
if self.name == "cython":
return "dict"
name = type(key).__name__ + ": " + type(value).__name__ + " dict"
return name
......
......@@ -30,56 +30,46 @@ from transonic.typing import format_type_as_backend_type
from .base import BackendAOT, TypeHintRemover, format_str
from .base_jit import SubBackendJIT
from .typing import base_type_formatter
from .typing import TypeFormatter
def analyze_array_type(type_):
"""Analyze an array type. return dtype, ndim"""
dtype, end = type_.split("[", 1)
if not dtype.startswith("np."):
dtype = "np." + dtype
def normalize_type_name_for_array(name):
if any(name.endswith(str(number)) for number in (32, 64, 128)):
return "np." + name
if name in ("int", "float", "complex"):
return "np." + name
return name
if ":" in end:
ndim = end.count(":")
else:
ndim = end.count("[") + 1
return dtype, ndim
class TypeFormatterCython(TypeFormatter):
def normalize_type_name(self, name):
if any(name.endswith(str(number)) for number in (32, 64, 128)):
return "np." + name + "_t"
if name in ("int", "float", "complex", "str"):
return f"cython.{name}"
return name
def make_array_code(self, dtype, ndim, memview):
dtype = normalize_type_name_for_array(dtype.__name__)
if ndim == 0:
return dtype
def memoryview_type(type_) -> str:
dtype, ndim = analyze_array_type(type_)
if ndim > 1:
end = "[" + ", ".join(":" * ndim) + "]"
else:
end = "[:]"
return f"{dtype}_t{end}"
def np_ndarray_type(type_) -> str:
dtype, ndim = analyze_array_type(type_)
return f"np.ndarray[{dtype + '_t'}, ndim={ndim}]"
def compute_cython_type_from_pythran_type(type_, memoryview=False):
if memview:
return memoryview_type(dtype, ndim)
else:
return np_ndarray_type(dtype, ndim)
if isinstance(type_, type):
type_ = format_type_as_backend_type(type_, base_type_formatter)
def make_dict_code(self, key, value):
return "dict"
if type_.endswith("]"):
if memoryview:
return memoryview_type(type_)
return np_ndarray_type(type_)
if any(type_.endswith(str(number)) for number in (32, 64, 128)):
return "np." + type_ + "_t"
def memoryview_type(dtype, ndim) -> str:
end = "[" + ", ".join(":" * ndim) + "]"
return f"{dtype}_t{end}"
if "dict" in type_:
return "dict"
return "cython." + type_
def np_ndarray_type(dtype, ndim) -> str:
return f"np.ndarray[{dtype}_t, ndim={ndim}]"
class HeaderFunction:
......@@ -157,12 +147,6 @@ class HeaderFunction:
class SubBackendJITCython(SubBackendJIT):
def compute_typename_from_object(self, obj: object):
"""return the Pythran type name"""
return compute_cython_type_from_pythran_type(
super().compute_typename_from_object(obj)
)
def make_new_header(self, func, arg_types):
# Include signature comming from type hints
header = HeaderFunction(
......@@ -176,10 +160,7 @@ class SubBackendJITCython(SubBackendJIT):
)
for signature in signatures:
header.add_signature(
compute_cython_type_from_pythran_type(type_)
for type_ in signature
)
header.add_signature(signature)
if arg_types != "no types":
header.add_signature(arg_types)
......@@ -204,6 +185,7 @@ class CythonBackend(BackendAOT):
suffix_header = ".pxd"
keyword_export = "cpdef"
_SubBackendJIT = SubBackendJITCython
_TypeFormatter = TypeFormatterCython
def _make_first_lines_header(self):
return ["import cython\n\nimport numpy as np\ncimport numpy as np\n"]
......@@ -339,9 +321,7 @@ class CythonBackend(BackendAOT):
name_type_args.append(name_type_arg)
possible_types = [x[index] for x in signatures_as_lists_strings]
for possible_type in set(possible_types):
ctypedef.append(
f" {compute_cython_type_from_pythran_type(possible_type)}\n"
)
ctypedef.append(f" {possible_type}\n")
ctypedef.sort()
ctypedef.insert(0, f"ctypedef fused {name_type_arg}:\n")
index += 1
......@@ -360,7 +340,7 @@ class CythonBackend(BackendAOT):
# TODO: fused types not supported here
# note: np.ndarray not supported by Cython in "locals"
locals_types = ", ".join(
f"{k}={compute_cython_type_from_pythran_type(v, memoryview=True)}"
f"{k}={format_type_as_backend_type(v, self.type_formatter, memview=True)}"
for k, v in locals_types.items()
)
signatures_func.append(f"@cython.locals({locals_types})")
......@@ -368,7 +348,9 @@ class CythonBackend(BackendAOT):
def_keyword = "cpdef"
if returns is not None:
returns = compute_cython_type_from_pythran_type(returns) + " "
returns = (
format_type_as_backend_type(returns, self.type_formatter) + " "
)
else:
returns = ""
......
from .cython import analyze_array_type
def test_float0():
dtype, ndim = analyze_array_type("float[]")
assert dtype == "np.float"
assert ndim == 1
def test_float1():
dtype, ndim = analyze_array_type("float[:]")
assert dtype == "np.float"
assert ndim == 1
......@@ -6,13 +6,19 @@ class TypeFormatter:
self.backend_name = backend_name
def normalize_type_name(self, name):
if self.backend_name == "cython":
return name
try:
return normalized_types[name]
except KeyError:
return name
def make_array_code(self, dtype, ndim, memview):
base = self.normalize_type_name(dtype.__name__)
if ndim == 0:
return base
return base + f"[{', '.join(':'*ndim)}]"
def make_dict_code(self, key, value):
return f"{key}: {value} dict"
base_type_formatter = TypeFormatter()
......@@ -22,7 +22,7 @@ from transonic.typing import format_type_as_backend_type, str2type
def _format_types_as_backend_types(types, backend_type_formatter, **kwargs):
"""Compute a list of pythran types
"""Compute a list of Pythran/Cython/... types
"""
backend_types = []
......@@ -44,7 +44,7 @@ def _format_types_as_backend_types(types, backend_type_formatter, **kwargs):
def compute_signatures_from_typeobjects(
types_in, backend_type_formatter
) -> List[List[str]]:
"""Compute a list of lists (signatures) of strings (pythran types)
"""Compute a list of lists (signatures) of strings (backend types)
"""
if isinstance(types_in, dict):
......@@ -62,19 +62,15 @@ def compute_signatures_from_typeobjects(
template_parameters.update(type_.get_template_parameters())
if not template_parameters:
str_types = []
for type_ in types:
if isinstance(type_, type):
str_type = type_.__name__
else:
str_type = type_
str_types.append(str_type)
if "_empty" in str_types:
if "_empty" in types:
raise ValueError(
"At least one annotation type lacking in a signature.\n"
f"types = {types}"
)
str_types = [
format_type_as_backend_type(type_, backend_type_formatter)
for type_ in types
]
return (str_types,)
if not all(param.values for param in template_parameters):
......
......@@ -9,6 +9,7 @@ from transonic.typing import (
ListMeta,
Dict,
DictMeta,
analyze_array_type,
)
from transonic.backends.typing import base_type_formatter
......@@ -68,3 +69,15 @@ def test_dict():
D = Dict[str, int]
assert isinstance(D, DictMeta)
assert D.format_as_backend_type(base_type_formatter) == "str: int dict"
def test_float0():
dtype, ndim = analyze_array_type("float[]")
assert dtype == "np.float"
assert ndim == 1
def test_float1():
dtype, ndim = analyze_array_type("float[:]")
assert dtype == "np.float"
assert ndim == 1
......@@ -327,10 +327,8 @@ class ArrayMeta(type):
if dtype is None or ndim is None:
raise ValueError
base = backend_type_formatter.normalize_type_name(dtype.__name__)
if ndim == 0:
return base
return base + f"[{', '.join([':']*ndim)}]"
memview = kwargs.get("memview", self.memview)
return backend_type_formatter.make_array_code(dtype, ndim, memview)
class Array(metaclass=ArrayMeta):
......@@ -471,7 +469,7 @@ class DictMeta(type):
value = format_type_as_backend_type(
self.type_values, backend_type_formatter, **kwargs
)
return f"{key}: {value} dict"
return backend_type_formatter.make_dict_code(key, value)
class Dict(metaclass=DictMeta):
......@@ -485,13 +483,20 @@ class Dict(metaclass=DictMeta):
def format_type_as_backend_type(type_, backend_type_formatter, **kwargs):
assert not isinstance(type_, str)
if isinstance(type_, str):
type_ = str2type(type_)
if hasattr(type_, "format_as_backend_type"):
backend_type = type_.format_as_backend_type(
backend_type_formatter, **kwargs
)
elif hasattr(type_, "__name__"):
backend_type = type_.__name__
else:
print(type_)
raise RuntimeError
assert backend_type is not None
return backend_type_formatter.normalize_type_name(backend_type)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment