• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

daisytuner / docc / 28760075767

06 Jul 2026 12:22AM UTC coverage: 62.801% (+0.3%) from 62.469%
28760075767

Pull #835

github

web-flow
Merge 90ef94753 into 6cd06aaae
Pull Request #835: [Python] Extends frontend support and adds pylulesh benchmarks

704 of 800 new or added lines in 5 files covered. (88.0%)

4 existing lines in 1 file now uncovered.

40479 of 64456 relevant lines covered (62.8%)

968.2 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

78.42
/python/docc/python/types/__init__.py
1
import ast
4✔
2
import inspect
4✔
3
import numpy as np
4✔
4

5
from typing import get_origin, get_args
4✔
6
from docc.sdfg import (
4✔
7
    PrimitiveType,
8
    Scalar,
9
    Pointer,
10
    Array,
11
    Structure,
12
    Tensor,
13
    Type,
14
)
15

16

17
def sdfg_type_from_type(python_type):
4✔
18
    if isinstance(python_type, Type):
4✔
19
        return python_type
×
20

21
    # Handle numpy.ndarray[Shape, python_type] type annotations
22
    if get_origin(python_type) is np.ndarray:
4✔
23
        args = get_args(python_type)
4✔
24
        if len(args) >= 2:
4✔
25
            elem_type = sdfg_type_from_type(args[1])
4✔
26
            return Pointer(elem_type)
4✔
27
        # Unparameterized ndarray defaults to void pointer
28
        return Pointer(Scalar(PrimitiveType.Void))
×
29

30
    # Handle np.dtype[ScalarType] annotations
31
    if get_origin(python_type) is np.dtype:
4✔
32
        return sdfg_type_from_type(get_args(python_type)[0])
4✔
33

34
    if python_type is float or python_type is np.float64:
4✔
35
        return Scalar(PrimitiveType.Double)
4✔
36
    elif python_type is np.float32:
4✔
37
        return Scalar(PrimitiveType.Float)
4✔
38
    elif python_type is bool or python_type is np.bool_:
4✔
39
        return Scalar(PrimitiveType.Bool)
4✔
40
    elif python_type is int or python_type is np.int64:
4✔
41
        return Scalar(PrimitiveType.Int64)
4✔
42
    elif python_type is np.int32:
4✔
43
        return Scalar(PrimitiveType.Int32)
4✔
44
    elif python_type is np.int16:
4✔
45
        return Scalar(PrimitiveType.Int16)
4✔
46
    elif python_type is np.int8:
4✔
47
        return Scalar(PrimitiveType.Int8)
4✔
48
    elif python_type is np.uint64:
4✔
49
        return Scalar(PrimitiveType.UInt64)
4✔
50
    elif python_type is np.uint32:
4✔
51
        return Scalar(PrimitiveType.UInt32)
4✔
52
    elif python_type is np.uint16:
4✔
53
        return Scalar(PrimitiveType.UInt16)
4✔
54
    elif python_type is np.uint8:
4✔
55
        return Scalar(PrimitiveType.UInt8)
4✔
56

57
    # Handle Python classes - map to Structure type
58
    if inspect.isclass(python_type):
4✔
59
        return Pointer(Structure(python_type.__name__))
4✔
60

61
    raise ValueError(f"Cannot map type to SDFG type: {python_type}")
×
62

63

64
def element_type_from_sdfg_type(sdfg_type: Type):
4✔
65
    if isinstance(sdfg_type, Scalar):
4✔
66
        return sdfg_type
4✔
67
    elif isinstance(sdfg_type, (Pointer, Array, Tensor)):
4✔
68
        return Scalar(sdfg_type.primitive_type)
4✔
69
    else:
70
        raise ValueError(
×
71
            f"Unsupported SDFG type for element type extraction: {sdfg_type}"
72
        )
73

74

75
def _element_type_from_dtype_value(value):
4✔
76
    """Map a runtime dtype value to an SDFG scalar element type.
77

78
    Accepts a numpy scalar type (e.g. ``np.float64``), a python type
79
    (``float``/``int``/``bool``), or an ``np.dtype`` instance. Returns None if
80
    the value is not a recognizable dtype.
81
    """
82
    try:
4✔
83
        np_dtype = np.dtype(value)
4✔
NEW
84
    except (TypeError, ValueError):
×
NEW
85
        return None
×
86
    try:
4✔
87
        return sdfg_type_from_type(np_dtype.type)
4✔
NEW
88
    except ValueError:
×
NEW
89
        return None
×
90

91

92
def element_type_from_ast_node(ast_node, container_table=None, globals_dict=None):
4✔
93
    # Default to double
94
    if ast_node is None:
4✔
95
        return Scalar(PrimitiveType.Double)
×
96

97
    # Handle python built-in types
98
    if isinstance(ast_node, ast.Name):
4✔
99
        if ast_node.id == "float":
4✔
100
            return Scalar(PrimitiveType.Double)
4✔
101
        if ast_node.id == "int":
4✔
102
            return Scalar(PrimitiveType.Int64)
4✔
103
        if ast_node.id == "bool":
4✔
104
            return Scalar(PrimitiveType.Bool)
×
105
        # Resolve a module-global dtype alias, e.g. `RealT = np.float64` used
106
        # as `np.zeros(shape, RealT)`. The global may hold a numpy scalar type,
107
        # a python type, or an np.dtype instance.
108
        if globals_dict is not None and ast_node.id in globals_dict:
4✔
109
            elem_type = _element_type_from_dtype_value(globals_dict[ast_node.id])
4✔
110
            if elem_type is not None:
4✔
111
                return elem_type
4✔
112

113
    # Handle complex types
114
    if isinstance(ast_node, ast.Attribute):
4✔
115
        # Handle numpy types like np.float64, np.int32, etc.
116
        if isinstance(ast_node.value, ast.Name) and ast_node.value.id in [
4✔
117
            "numpy",
118
            "np",
119
        ]:
120
            if ast_node.attr == "float64":
4✔
121
                return Scalar(PrimitiveType.Double)
4✔
122
            if ast_node.attr == "float32":
4✔
123
                return Scalar(PrimitiveType.Float)
4✔
124
            if ast_node.attr == "int64":
4✔
125
                return Scalar(PrimitiveType.Int64)
4✔
126
            if ast_node.attr == "int32":
4✔
127
                return Scalar(PrimitiveType.Int32)
4✔
128
            if ast_node.attr == "int16":
×
129
                return Scalar(PrimitiveType.Int16)
×
130
            if ast_node.attr == "int8":
×
131
                return Scalar(PrimitiveType.Int8)
×
132
            if ast_node.attr == "uint64":
×
133
                return Scalar(PrimitiveType.UInt64)
×
134
            if ast_node.attr == "uint32":
×
135
                return Scalar(PrimitiveType.UInt32)
×
136
            if ast_node.attr == "uint16":
×
137
                return Scalar(PrimitiveType.UInt16)
×
138
            if ast_node.attr == "uint8":
×
139
                return Scalar(PrimitiveType.UInt8)
×
140
            if ast_node.attr == "bool_":
×
141
                return Scalar(PrimitiveType.Bool)
×
142

143
        # Handle arr.dtype - get element type from array's type in symbol table
144
        if ast_node.attr == "dtype" and container_table is not None:
4✔
145
            if isinstance(ast_node.value, ast.Name):
4✔
146
                var_name = ast_node.value.id
4✔
147
                if var_name in container_table:
4✔
148
                    var_type = container_table[var_name]
4✔
149
                    return element_type_from_sdfg_type(var_type)
4✔
150

151
    raise ValueError(f"Cannot map AST node to SDFG type: {ast.dump(ast_node)}")
×
152

153

154
def promote_element_types(left_element_type, right_element_type):
4✔
155
    """
156
    Promote two dtypes following NumPy rules for array-array operations.
157

158
    Rules:
159
    - float + float → wider float
160
    - int + int → wider int
161
    - float + int → float that can represent both (float32+int32 → float64)
162
    """
163
    left_pt = left_element_type.primitive_type
4✔
164
    right_pt = right_element_type.primitive_type
4✔
165

166
    # Check if types are floating point (includes half-precision types)
167
    float_types = {
4✔
168
        PrimitiveType.Double,
169
        PrimitiveType.Float,
170
        PrimitiveType.Half,
171
        PrimitiveType.BFloat,
172
    }
173
    int_types = {
4✔
174
        PrimitiveType.Int64,
175
        PrimitiveType.Int32,
176
        PrimitiveType.Int16,
177
        PrimitiveType.Int8,
178
        PrimitiveType.UInt64,
179
        PrimitiveType.UInt32,
180
        PrimitiveType.UInt16,
181
        PrimitiveType.UInt8,
182
    }
183

184
    left_is_float = left_pt in float_types
4✔
185
    right_is_float = right_pt in float_types
4✔
186

187
    # Both floats: return wider
188
    if left_is_float and right_is_float:
4✔
189
        if left_pt == PrimitiveType.Double or right_pt == PrimitiveType.Double:
4✔
190
            return Scalar(PrimitiveType.Double)
4✔
191
        if left_pt == PrimitiveType.Float or right_pt == PrimitiveType.Float:
4✔
192
            return Scalar(PrimitiveType.Float)
4✔
193
        # Half-precision types: same type stays, mixed promotes to Float
194
        if left_pt == right_pt:
4✔
195
            return Scalar(left_pt)  # BFloat+BFloat→BFloat, Half+Half→Half
4✔
196
        return Scalar(PrimitiveType.Float)  # Mixed half types → float32
×
197

198
    # Both integers: return wider (simplified - always Int64 for now)
199
    if not left_is_float and not right_is_float:
4✔
200
        if left_pt == PrimitiveType.Int64 or right_pt == PrimitiveType.Int64:
4✔
201
            return Scalar(PrimitiveType.Int64)
4✔
202
        if left_pt == PrimitiveType.UInt64 or right_pt == PrimitiveType.UInt64:
4✔
203
            return Scalar(PrimitiveType.Int64)  # Promote to signed for safety
×
204
        if left_pt == PrimitiveType.Int32 or right_pt == PrimitiveType.Int32:
4✔
205
            return Scalar(PrimitiveType.Int32)
4✔
206
        return Scalar(PrimitiveType.Int64)  # Default
×
207

208
    # Mixed float + int: need a float that can represent the int
209
    # float32 can represent int16/int8, but not int32
210
    # float64 can represent int32 and smaller
211
    # half types + int → promote to float32 (half can't represent ints well)
212
    float_type = left_pt if left_is_float else right_pt
4✔
213
    int_type = right_pt if left_is_float else left_pt
4✔
214

215
    # If float is already double, use double
216
    if float_type == PrimitiveType.Double:
4✔
217
        return Scalar(PrimitiveType.Double)
4✔
218

219
    # Half-precision + any int → float32 (half types can't represent ints well)
220
    if float_type in {PrimitiveType.Half, PrimitiveType.BFloat}:
4✔
221
        return Scalar(PrimitiveType.Float)
×
222

223
    # float32 + (int32 or larger) → float64
224
    if int_type in {
4✔
225
        PrimitiveType.Int32,
226
        PrimitiveType.Int64,
227
        PrimitiveType.UInt32,
228
        PrimitiveType.UInt64,
229
    }:
230
        return Scalar(PrimitiveType.Double)
4✔
231

232
    # float32 + (int16 or smaller) → float32
233
    return Scalar(PrimitiveType.Float)
×
234

235

236
def numpy_promote_types(left_type, left_is_array, right_type, right_is_array):
4✔
237
    """
238
    Implement NumPy's type promotion rules for binary operations.
239

240
    Key rule: Scalars adapt to arrays, not vice versa.
241
    - array + scalar → array's dtype (scalar is cast to array's dtype)
242
    - array + array → standard promotion (wider/float wins)
243
    - scalar + scalar → standard promotion
244

245
    Args:
246
        left_type: Element type of left operand (Scalar)
247
        left_is_array: True if left operand is an array
248
        right_type: Element type of right operand (Scalar)
249
        right_is_array: True if right operand is an array
250

251
    Returns:
252
        Result element type (Scalar)
253
    """
254
    if left_is_array and not right_is_array:
4✔
255
        # Scalar adapts to array
256
        return left_type
4✔
257
    if right_is_array and not left_is_array:
4✔
258
        # Scalar adapts to array
259
        return right_type
4✔
260
    # Both arrays or both scalars: use standard promotion
261
    return promote_element_types(left_type, right_type)
4✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc