halide 19.0.0__cp311-cp311-macosx_11_0_arm64.whl → 21.0.0__cp311-cp311-macosx_11_0_arm64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. halide/__init__.py +10 -6
  2. halide/_generator_helpers.py +190 -127
  3. halide/bin/adams2019_retrain_cost_model +0 -0
  4. halide/bin/anderson2021_retrain_cost_model +0 -0
  5. halide/bin/gengen +0 -0
  6. halide/bin/get_host_target +0 -0
  7. halide/halide_.cpython-311-darwin.so +0 -0
  8. halide/imageio.py +1 -1
  9. halide/include/Halide.h +1775 -1477
  10. halide/include/HalideBuffer.h +13 -13
  11. halide/include/HalidePyTorchCudaHelpers.h +1 -1
  12. halide/include/HalideRuntime.h +35 -16
  13. halide/lib/cmake/Halide/FindHalide_LLVM.cmake +44 -15
  14. halide/lib/cmake/Halide/FindV8.cmake +0 -12
  15. halide/lib/cmake/Halide/Halide-shared-targets.cmake +1 -1
  16. halide/lib/cmake/Halide/HalideConfig.cmake +1 -1
  17. halide/lib/cmake/Halide/HalideConfigVersion.cmake +3 -3
  18. halide/lib/cmake/HalideHelpers/Halide-Interfaces.cmake +1 -0
  19. halide/lib/cmake/HalideHelpers/HalideGeneratorHelpers.cmake +31 -9
  20. halide/lib/cmake/HalideHelpers/HalideHelpersConfigVersion.cmake +3 -3
  21. halide/lib/cmake/Halide_Python/Halide_PythonConfigVersion.cmake +3 -3
  22. halide/lib/libHalide.dylib +0 -0
  23. halide/lib/libHalidePyStubs.a +0 -0
  24. halide/lib/libHalide_GenGen.a +0 -0
  25. halide/lib/libautoschedule_adams2019.so +0 -0
  26. halide/lib/libautoschedule_anderson2021.so +0 -0
  27. halide/lib/libautoschedule_li2018.so +0 -0
  28. halide/lib/libautoschedule_mullapudi2016.so +0 -0
  29. halide/share/doc/Halide/README.md +7 -6
  30. halide/share/doc/Halide/doc/BuildingHalideWithCMake.md +78 -6
  31. halide/share/doc/Halide/doc/HalideCMakePackage.md +9 -2
  32. halide/share/doc/Halide/doc/Python.md +19 -4
  33. halide/share/doc/Halide/doc/RunGen.md +1 -1
  34. {halide-19.0.0.data → halide-21.0.0.data}/data/share/cmake/Halide/HalideConfig.cmake +4 -1
  35. {halide-19.0.0.data → halide-21.0.0.data}/data/share/cmake/Halide/HalideConfigVersion.cmake +3 -3
  36. {halide-19.0.0.data → halide-21.0.0.data}/data/share/cmake/HalideHelpers/HalideHelpersConfig.cmake +4 -1
  37. {halide-19.0.0.data → halide-21.0.0.data}/data/share/cmake/HalideHelpers/HalideHelpersConfigVersion.cmake +3 -3
  38. halide-21.0.0.dist-info/METADATA +302 -0
  39. {halide-19.0.0.dist-info → halide-21.0.0.dist-info}/RECORD +41 -41
  40. {halide-19.0.0.dist-info → halide-21.0.0.dist-info}/WHEEL +1 -1
  41. halide-19.0.0.dist-info/METADATA +0 -301
  42. {halide-19.0.0.dist-info → halide-21.0.0.dist-info}/licenses/LICENSE.txt +0 -0
@@ -3,15 +3,35 @@ from abc import ABC, abstractmethod
3
3
  from contextvars import ContextVar
4
4
  from enum import Enum
5
5
  from functools import total_ordering
6
- from .halide_ import *
7
- from .halide_ import _unique_name, _UnspecifiedType
6
+ from .halide_ import (
7
+ ArgInfo,
8
+ ArgInfoDirection,
9
+ ArgInfoKind,
10
+ Bool,
11
+ Buffer,
12
+ Expr,
13
+ Float,
14
+ Func,
15
+ GeneratorContext,
16
+ HalideError,
17
+ ImageParam,
18
+ Int,
19
+ Param,
20
+ Pipeline,
21
+ Type,
22
+ UInt,
23
+ Var,
24
+ _,
25
+ _UnspecifiedType,
26
+ _unique_name,
27
+ )
8
28
  from inspect import isclass
9
29
  from typing import Any, Optional
10
- import builtins
11
30
  import re
12
31
  import sys
13
32
  import warnings
14
33
 
34
+
15
35
  def _fail(msg: str):
16
36
  raise HalideError(msg)
17
37
 
@@ -20,12 +40,14 @@ def _check(cond: bool, msg: str):
20
40
  if not cond:
21
41
  _fail(msg)
22
42
 
43
+
23
44
  # Basically, a valid C identifier, except:
24
45
  #
25
46
  # -- initial _ is forbidden (rather than merely "reserved")
26
47
  # -- two underscores in a row is also forbidden
27
48
  _NAME_RE = re.compile(r"^(?!.*__)[A-Za-z0-9][A-Za-z0-9_]*$")
28
49
 
50
+
29
51
  def _is_valid_name(name: str) -> bool:
30
52
  if not name:
31
53
  return False
@@ -39,7 +61,7 @@ def _is_valid_name(name: str) -> bool:
39
61
  def _check_valid_name(name: str) -> str:
40
62
  _check(
41
63
  _is_valid_name(name),
42
- "The name '%s' is not valid for a GeneratorParam, Input, or Output." % name,
64
+ f"The name '{name}' is not valid for a GeneratorParam, Input, or Output.",
43
65
  )
44
66
  return name
45
67
 
@@ -57,17 +79,16 @@ def _sanitize_type(t: object) -> Type:
57
79
  elif t is float:
58
80
  return Float(32)
59
81
  else:
60
- _check(isinstance(t, Type), "Expected a Halide Type, but saw: %s" % t)
82
+ _check(isinstance(t, Type), f"Expected a Halide Type, but saw: {t}")
61
83
  return t
62
84
 
85
+
63
86
  def _normalize_type_list(types: object) -> list[Type]:
64
87
  # Always treat _UnspecifiedType as a non-type
65
- if types is None:
66
- types = []
67
- elif isinstance(types, Type) and types == _UnspecifiedType():
88
+ if types is None or (isinstance(types, Type) and types == _UnspecifiedType()):
68
89
  types = []
69
90
  if type(types) is not list:
70
- types = [types];
91
+ types = [types]
71
92
  types = [_sanitize_type(t) for t in types]
72
93
  return types
73
94
 
@@ -88,7 +109,7 @@ _type_string_map = {
88
109
 
89
110
 
90
111
  def _parse_halide_type(s: str) -> Type:
91
- _check(s in _type_string_map, "The value %s cannot be parsed as a Type." % s)
112
+ _check(s in _type_string_map, f"The value {s} cannot be parsed as a Type.")
92
113
  return _type_string_map[s]
93
114
 
94
115
 
@@ -97,31 +118,22 @@ def _parse_halide_type_list(s: str) -> list[Type]:
97
118
 
98
119
 
99
120
  class Requirement:
100
- # Name
101
- _name: str = ""
102
-
103
- # List of the required types, if any. An empty list means
104
- # no constraints. The list will usually be a single type,
105
- # except for Outputs that have Tuple-valued results, which
106
- # can have multiple types.
107
- _types: list[Type] = []
108
-
109
- # Required dimensions. 0 = scalar. -1 = unconstrained.
110
- _dimensions: int = -1
111
-
112
121
  def __init__(self, name: str, types: object, dimensions: int):
113
122
  self._name = _check_valid_name(name)
123
+ # List of the required types, if any. An empty list means
124
+ # no constraints. The list will usually be a single type,
125
+ # except for Outputs that have Tuple-valued results, which
126
+ # can have multiple types.
114
127
  self._types = _normalize_type_list(types)
128
+ # Required dimensions. 0 = scalar. -1 = unconstrained.
115
129
  self._dimensions = dimensions
116
130
  _check(
117
131
  len(self._types) > 0,
118
- "No type has been specified for %s. Try specifying one in code, or by setting '%s.type' as a GeneratorParam."
119
- % (self._name, self._name),
132
+ f"No type has been specified for {self._name}. Try specifying one in code, or by setting '{self._name}.type' as a GeneratorParam.",
120
133
  )
121
134
  _check(
122
135
  self._dimensions >= 0,
123
- "No dimensionality has been specified for %s. Try specifying one in code, or by setting '%s.dim' as a GeneratorParam."
124
- % (self._name, self._name),
136
+ f"No dimensionality has been specified for {self._name}. Try specifying one in code, or by setting '{self._name}.dim' as a GeneratorParam.",
125
137
  )
126
138
  self._check_types_and_dimensions(types, dimensions)
127
139
 
@@ -132,16 +144,16 @@ class Requirement:
132
144
 
133
145
  _check(
134
146
  len(types) == len(self._types),
135
- "Type mismatch for %s: expected %d types but saw %d" % (self._name, len(self._types), len(types)),
147
+ f"Type mismatch for {self._name}: expected {len(self._types)} types but saw {len(types)}",
136
148
  )
137
149
  for i in range(0, len(types)):
138
150
  _check(
139
151
  self._types[i] == types[i],
140
- "Type mismatch for %s:%d: expected %s saw %s" % (self._name, i, self._types[i], types[i]),
152
+ f"Type mismatch for {self._name}:{i}: expected {self._types[i]} saw {types[i]}",
141
153
  )
142
154
  _check(
143
155
  dimensions == self._dimensions,
144
- "Dimensions mismatch for %s: expected %d saw %d" % (self._name, self._dimensions, dimensions),
156
+ f"Dimensions mismatch for {self._name}: expected {self._dimensions} saw {dimensions}",
145
157
  )
146
158
 
147
159
 
@@ -151,7 +163,7 @@ class GeneratorParam:
151
163
  # on this object. (We don't, actually, but it defeats
152
164
  # PyType's complaints about using len(), arithmetic operators, etc.
153
165
  # on GeneratorParam fields, since they are replaced at runtime.)
154
- _HAS_DYNAMIC_ATTRIBUTES:bool = True
166
+ _HAS_DYNAMIC_ATTRIBUTES: bool = True
155
167
 
156
168
  _name: str = ""
157
169
  _value: Any = None
@@ -177,7 +189,7 @@ class GeneratorParam:
177
189
  value = _parse_halide_type_list(str(value))
178
190
  return value
179
191
  else:
180
- _fail("GeneratorParam %s has unsupported type %s" % (name, gp_type))
192
+ _fail(f"GeneratorParam {name} has unsupported type {gp_type}")
181
193
  return None
182
194
 
183
195
  def _make_replacement(self, value: Any, r: Requirement) -> Any:
@@ -192,23 +204,22 @@ class GeneratorParam:
192
204
  else:
193
205
  return self._value
194
206
 
195
- def _get_types_and_dimensions(self) -> (list[Type], int):
207
+ def _get_types_and_dimensions(self) -> tuple[list[Type], int]:
196
208
  # Use dummy type-and-dimensions here so that the ctor won't fail due to undefined status
197
209
  return [Int(32)], 0
198
210
 
199
211
 
200
212
  class InputBuffer(ImageParam):
201
-
202
213
  def __init__(self, type: Optional[Type], dimensions: Optional[int]):
203
214
  type = _sanitize_type(type)
204
215
  if dimensions is None:
205
216
  dimensions = -1
206
217
  super().__init__(type, dimensions, _unique_name())
207
218
 
208
- def _get_types_and_dimensions(self) -> (list[Type], int):
219
+ def _get_types_and_dimensions(self) -> tuple[list[Type], int]:
209
220
  return _normalize_type_list(self.type()), self.dimensions()
210
221
 
211
- def _get_direction_and_kind(self) -> (ArgInfoDirection, ArgInfoKind):
222
+ def _get_direction_and_kind(self) -> tuple[ArgInfoDirection, ArgInfoKind]:
212
223
  return ArgInfoDirection.Input, ArgInfoKind.Buffer
213
224
 
214
225
  def _make_replacement(self, value: Any, r: Requirement) -> ImageParam:
@@ -218,11 +229,11 @@ class InputBuffer(ImageParam):
218
229
 
219
230
  _check(
220
231
  isinstance(value, (Buffer, ImageParam)),
221
- "Input %s requires an ImageParam or Buffer argument when using call(), but saw %s" % (r._name, str(value)),
232
+ f"Input {r._name} requires an ImageParam or Buffer argument when using call(), but saw {value}",
222
233
  )
223
234
  _check(
224
235
  value.defined(),
225
- "Cannot set the value for %s to an undefined value." % r._name,
236
+ f"Cannot set the value for {r._name} to an undefined value.",
226
237
  )
227
238
  if isinstance(value, Buffer):
228
239
  im = ImageParam(value.type(), value.dimensions(), r._name)
@@ -233,15 +244,14 @@ class InputBuffer(ImageParam):
233
244
 
234
245
 
235
246
  class InputScalar(Param):
236
-
237
247
  def __init__(self, type: Optional[Type]):
238
248
  type = _sanitize_type(type)
239
249
  super().__init__(type, _unique_name())
240
250
 
241
- def _get_types_and_dimensions(self) -> (list[Type], int):
251
+ def _get_types_and_dimensions(self) -> tuple[list[Type], int]:
242
252
  return _normalize_type_list(self.type()), 0
243
253
 
244
- def _get_direction_and_kind(self) -> (ArgInfoDirection, ArgInfoKind):
254
+ def _get_direction_and_kind(self) -> tuple[ArgInfoDirection, ArgInfoKind]:
245
255
  return ArgInfoDirection.Input, ArgInfoKind.Scalar
246
256
 
247
257
  def _make_replacement(self, value: Any, r: Requirement) -> Param:
@@ -256,19 +266,17 @@ class InputScalar(Param):
256
266
  else:
257
267
  _check(
258
268
  isinstance(value, Param),
259
- "Input %s requires a Param (or scalar literal) argument when using call(), but saw %s." %
260
- (r._name, str(value)),
269
+ f"Input {r._name} requires a Param (or scalar literal) argument when using call(), but saw {value}.",
261
270
  )
262
271
  _check(
263
272
  value.defined(),
264
- "Cannot set the value for %s to an undefined value." % r._name,
273
+ f"Cannot set the value for {r._name} to an undefined value.",
265
274
  )
266
275
  r._check_types_and_dimensions([value.type()], 0)
267
276
  return value
268
277
 
269
278
 
270
279
  class OutputBuffer(Func):
271
-
272
280
  def __init__(self, types: Optional[Type], dimensions: Optional[int]):
273
281
  types = _normalize_type_list(types)
274
282
  if dimensions is None:
@@ -277,10 +285,10 @@ class OutputBuffer(Func):
277
285
  self._types = types
278
286
  self._dimensions = dimensions
279
287
 
280
- def _get_types_and_dimensions(self) -> (list[Type], int):
288
+ def _get_types_and_dimensions(self) -> tuple[list[Type], int]:
281
289
  return self._types, self._dimensions
282
290
 
283
- def _get_direction_and_kind(self) -> (ArgInfoDirection, ArgInfoKind):
291
+ def _get_direction_and_kind(self) -> tuple[ArgInfoDirection, ArgInfoKind]:
284
292
  return ArgInfoDirection.Output, ArgInfoKind.Buffer
285
293
 
286
294
  def _make_replacement(self, value: Any, r: Requirement) -> Func:
@@ -290,14 +298,16 @@ class OutputBuffer(Func):
290
298
 
291
299
  _check(
292
300
  isinstance(value, (Buffer, Func)),
293
- "Output %s requires a Func or Buffer argument when using call(), but saw %s" % (r._name, str(value)),
301
+ f"Output {r._name} requires a Func or Buffer argument when using call(), but saw {value}",
294
302
  )
295
303
  _check(
296
304
  value.defined(),
297
- "Cannot set the value for %s to an undefined value." % r._name,
305
+ f"Cannot set the value for {r._name} to an undefined value.",
298
306
  )
299
307
 
300
- value_types = [value.type()] if isinstance(value, Buffer) else value.output_types
308
+ value_types = (
309
+ [value.type()] if isinstance(value, Buffer) else value.output_types
310
+ )
301
311
  r._check_types_and_dimensions(value_types, value.dimensions())
302
312
 
303
313
  if isinstance(value, Buffer):
@@ -313,7 +323,6 @@ class OutputBuffer(Func):
313
323
 
314
324
  # OutputScalar is just like OutputBuffer, except it is always dimensions = 0
315
325
  class OutputScalar(OutputBuffer):
316
-
317
326
  def __init__(self, types: Optional[Type]):
318
327
  super().__init__(types, 0)
319
328
 
@@ -324,11 +333,11 @@ class OutputScalar(OutputBuffer):
324
333
 
325
334
  _check(
326
335
  isinstance(value, Expr),
327
- "Output %s requires an Expr argument when using call(), but saw %s" % (r._name, str(value)),
336
+ f"Output {r._name} requires an Expr argument when using call(), but saw {value}",
328
337
  )
329
338
  _check(
330
339
  value.defined(),
331
- "Cannot set the value for %s to an undefined value." % r._name,
340
+ f"Cannot set the value for {r._name} to an undefined value.",
332
341
  )
333
342
  r._check_types_and_dimensions(value.type(), 0)
334
343
  f = Func(value.type(), 0, r._name)
@@ -359,11 +368,10 @@ def _unsorted_cls_dir(cls):
359
368
  # Don't use dir(): it will alphabetize the result. It's critical that
360
369
  # we produce this list in order of declaration, which __dict__ should guarantee
361
370
  # in Python 3.7+.
362
- for k, v in cls.__dict__.items():
363
- yield (k, v)
371
+ yield from cls.__dict__.items()
364
372
 
365
373
 
366
- _halide_generator_context = ContextVar('halide_generator_context', default=None)
374
+ _halide_generator_context = ContextVar("halide_generator_context", default=None)
367
375
 
368
376
 
369
377
  def _generatorcontext_enter(self: GeneratorContext) -> GeneratorContext:
@@ -414,34 +422,35 @@ class Generator(ABC):
414
422
 
415
423
  _check(
416
424
  len(args) <= len(generator._arginfos_in),
417
- "Generator '%s' allows at most %d positional args, but %d were specified." %
418
- (generator._get_name(), len(generator._arginfos_in), len(args)))
425
+ f"Generator '{generator._get_name()}' allows at most {len(generator._arginfos_in)} positional args, but {len(args)} were specified.",
426
+ )
419
427
 
420
428
  inputs_set = []
421
429
  for i in range(0, len(args)):
422
430
  a = generator._arginfos_in[i]
423
431
  k = a.name
424
432
  v = args[i]
425
- _check(k not in inputs_set, "Input %s was specified multiple times." % k)
433
+ _check(k not in inputs_set, f"Input {k} was specified multiple times.")
426
434
  inputs_set.append(k)
427
435
  generator._bind_input(k, [v])
428
436
 
429
437
  input_names = [a.name for a in generator._arginfos_in]
430
438
  for k, v in kwargs.items():
431
- _check(k in input_names, "Unknown input '%s' specified via keyword argument." % k)
432
- _check(k not in inputs_set, "Input %s specified multiple times." % k)
439
+ _check(
440
+ k in input_names, f"Unknown input '{k}' specified via keyword argument."
441
+ )
442
+ _check(k not in inputs_set, f"Input {k} specified multiple times.")
433
443
  inputs_set.append(k)
434
444
  generator._bind_input(k, [v])
435
445
 
436
446
  _check(
437
- len(inputs_set) == len(generator._arginfos_in), "Generator '%s' requires %d args, but %d were specified." %
438
- (generator._get_name(), len(generator._arginfos_in), len(inputs_set)))
447
+ len(inputs_set) == len(generator._arginfos_in),
448
+ f"Generator '{generator._get_name()}' requires {len(generator._arginfos_in)} args, but {len(inputs_set)} were specified.",
449
+ )
439
450
 
440
451
  generator._build_pipeline()
441
452
 
442
- outputs = []
443
- for o in generator._arginfos_out:
444
- outputs.append(generator._get_output_func(o.name))
453
+ outputs = [generator._get_output_func(o.name) for o in generator._arginfos_out]
445
454
 
446
455
  return outputs[0] if len(outputs) == 1 else tuple(outputs)
447
456
 
@@ -458,8 +467,14 @@ class Generator(ABC):
458
467
  def __setattr__(self, name, value):
459
468
  r = getattr(self, "_requirements", None)
460
469
  s = getattr(self, "_stage", None)
461
- if r and s and (name in r) and (s != _Stage.configure_called) and (s != _Stage.gp_created):
462
- raise AttributeError("Invalid write to field '%s'" % name)
470
+ if (
471
+ r
472
+ and s
473
+ and (name in r)
474
+ and (s != _Stage.configure_called)
475
+ and (s != _Stage.gp_created)
476
+ ):
477
+ raise AttributeError(f"Invalid write to field '{name}'")
463
478
  super().__setattr__(name, value)
464
479
 
465
480
  def __init__(self, generator_params: dict = {}):
@@ -484,7 +499,9 @@ class Generator(ABC):
484
499
 
485
500
  self._advance_to_gp_created()
486
501
  if generator_params:
487
- _check(isinstance(generator_params, dict), "generator_params must be a dict")
502
+ _check(
503
+ isinstance(generator_params, dict), "generator_params must be a dict"
504
+ )
488
505
  for k, v in generator_params.items():
489
506
  self._set_generatorparam_value(k, v)
490
507
 
@@ -502,30 +519,48 @@ class Generator(ABC):
502
519
  _check_valid_name(name)
503
520
  _check(
504
521
  name not in self._requirements,
505
- "The name '%s' is used more than once in Generator %s." % (name, self._get_name()),
522
+ f"The name '{name}' is used more than once in Generator {self._get_name()}.",
506
523
  )
507
524
  types, dimensions = io._get_types_and_dimensions()
508
- types, dimensions = self._set_io_types_and_dimensions_from_gp(name, types, dimensions)
525
+ types, dimensions = self._set_io_types_and_dimensions_from_gp(
526
+ name, types, dimensions
527
+ )
509
528
  r = Requirement(name, types, dimensions)
510
529
  self._requirements[name] = r
511
530
  io_dict[name] = io
512
531
  if arginfos is not None:
513
- arginfos.append(ArgInfo(name, *io._get_direction_and_kind(), types, dimensions))
532
+ arginfos.append(
533
+ ArgInfo(name, *io._get_direction_and_kind(), types, dimensions)
534
+ )
514
535
 
515
536
  def add_input(self, name: str, io) -> None:
516
- _check(self._in_configure > 0, "Can only call add_input() from the configure() method.")
517
- _check(not hasattr(self, name),
518
- "Cannot call add_input('%s') because the class already has a member of that name." % name)
519
- _check(isinstance(io, (InputBuffer, InputScalar)),
520
- "Cannot call add_input() with an object of type '%s'." % type(io))
537
+ _check(
538
+ self._in_configure > 0,
539
+ "Can only call add_input() from the configure() method.",
540
+ )
541
+ _check(
542
+ not hasattr(self, name),
543
+ f"Cannot call add_input('{name}') because the class already has a member of that name.",
544
+ )
545
+ _check(
546
+ isinstance(io, (InputBuffer, InputScalar)),
547
+ f"Cannot call add_input() with an object of type '{type(io)}'.",
548
+ )
521
549
  self._add_gpio(name, io, self._inputs_dict, self._arginfos_in)
522
550
 
523
551
  def add_output(self, name: str, io) -> None:
524
- _check(self._in_configure > 0, "Can only call add_output() from the configure() method.")
525
- _check(not hasattr(self, name),
526
- "Cannot call add_output('%s') because the class already has a member of that name." % name)
527
- _check(isinstance(io, (OutputBuffer, OutputScalar)),
528
- "Cannot call add_output() with an object of type '%s'." % type(io))
552
+ _check(
553
+ self._in_configure > 0,
554
+ "Can only call add_output() from the configure() method.",
555
+ )
556
+ _check(
557
+ not hasattr(self, name),
558
+ f"Cannot call add_output('{name}') because the class already has a member of that name.",
559
+ )
560
+ _check(
561
+ isinstance(io, (OutputBuffer, OutputScalar)),
562
+ f"Cannot call add_output() with an object of type '{type(io)}'.",
563
+ )
529
564
  self._add_gpio(name, io, self._outputs_dict, self._arginfos_out)
530
565
 
531
566
  def _advance_to_stage(self, new_stage: _Stage):
@@ -538,7 +573,6 @@ class Generator(ABC):
538
573
  }
539
574
  assert new_stage in _stage_advancers
540
575
  a = _stage_advancers[new_stage]
541
- old_stage = self._stage
542
576
  if self._stage < new_stage:
543
577
  a()
544
578
  assert self._stage >= new_stage
@@ -578,11 +612,17 @@ class Generator(ABC):
578
612
  if not (is_input or is_output):
579
613
  continue
580
614
 
581
- if is_input and outputs_seen and not self.allow_out_of_order_inputs_and_outputs():
582
- io_order_warning = ("Generators will always produce code that orders all Inputs before all Outputs; "
583
- "this Generator declares the Inputs and Outputs in a different order, so the calling convention may not be as expected. "
584
- "A future version of Halide will make this illegal, and require all Inputs to be declared before all Outputs. "
585
- "(You can avoid this requirement by overriding Generator::allow_out_of_order_inputs_and_outputs().)")
615
+ if (
616
+ is_input
617
+ and outputs_seen
618
+ and not self.allow_out_of_order_inputs_and_outputs()
619
+ ):
620
+ io_order_warning = (
621
+ "Generators will always produce code that orders all Inputs before all Outputs; "
622
+ "this Generator declares the Inputs and Outputs in a different order, so the calling convention may not be as expected. "
623
+ "A future version of Halide will make this illegal, and require all Inputs to be declared before all Outputs. "
624
+ "(You can avoid this requirement by overriding Generator::allow_out_of_order_inputs_and_outputs().)"
625
+ )
586
626
  warnings.warn(io_order_warning)
587
627
 
588
628
  if is_output:
@@ -593,35 +633,33 @@ class Generator(ABC):
593
633
 
594
634
  _check(
595
635
  len(self._unhandled_generator_params) == 0,
596
- "Generator %s has no GeneratorParam(s) named: %s" %
597
- (self._get_name(), list(self._unhandled_generator_params.keys())),
636
+ f"Generator {self._get_name()} has no GeneratorParam(s) named: {list(self._unhandled_generator_params.keys())}",
598
637
  )
599
638
 
600
639
  self._stage = _Stage.io_created
601
640
 
602
- def _set_io_types_and_dimensions_from_gp(self, name: str, current_types: list[Type],
603
- current_dimensions: int) -> (list[Type], int):
641
+ def _set_io_types_and_dimensions_from_gp(
642
+ self, name: str, current_types: list[Type], current_dimensions: int
643
+ ) -> tuple[list[Type], int]:
604
644
  new_types = current_types
605
645
  new_dimensions = current_dimensions
606
646
 
607
- type_name = "%s.type" % name
647
+ type_name = f"{name}.type"
608
648
  if type_name in self._unhandled_generator_params:
609
649
  value = self._unhandled_generator_params.pop(type_name)
610
650
  new_types = GeneratorParam._parse_value(type_name, Type, value)
611
651
  _check(
612
652
  len(current_types) == 0,
613
- "Cannot set the GeneratorParam %s for %s because the value is explicitly specified in the Python source."
614
- % (type_name, self._get_name()),
653
+ f"Cannot set the GeneratorParam {type_name} for {self._get_name()} because the value is explicitly specified in the Python source.",
615
654
  )
616
655
 
617
- dimensions_name = "%s.dim" % name
656
+ dimensions_name = f"{name}.dim"
618
657
  if dimensions_name in self._unhandled_generator_params:
619
658
  value = self._unhandled_generator_params.pop(dimensions_name)
620
659
  new_dimensions = GeneratorParam._parse_value(dimensions_name, int, value)
621
660
  _check(
622
661
  current_dimensions == -1,
623
- "Cannot set the GeneratorParam %s for %s because the value is explicitly specified in the Python source."
624
- % (dimensions_name, self._get_name()),
662
+ f"Cannot set the GeneratorParam {dimensions_name} for {self._get_name()} because the value is explicitly specified in the Python source.",
625
663
  )
626
664
 
627
665
  return new_types, new_dimensions
@@ -635,7 +673,7 @@ class Generator(ABC):
635
673
 
636
674
  _check(
637
675
  len(self._outputs_dict) > 0,
638
- "Generator '%s' must declare at least one output." % self._get_name(),
676
+ f"Generator '{self._get_name()}' must declare at least one output.",
639
677
  )
640
678
 
641
679
  self._stage = _Stage.configure_called
@@ -670,7 +708,7 @@ class Generator(ABC):
670
708
  def _set_generatorparam_value(self, name: str, value: Any):
671
709
  _check(
672
710
  name != "target",
673
- "The GeneratorParam named %s cannot be set by set_generatorparam_value." % name,
711
+ f"The GeneratorParam named {name} cannot be set by set_generatorparam_value.",
674
712
  )
675
713
  assert self._stage == _Stage.gp_created
676
714
  assert not self._pipeline
@@ -682,12 +720,17 @@ class Generator(ABC):
682
720
  # Do not mutate the existing GP in place; it could be shared across multiple Generators.
683
721
  self._gp_dict[name] = GeneratorParam(new_value)
684
722
  elif name == "autoscheduler":
685
- _check(not self.autoscheduler().name, "The GeneratorParam %s cannot be set more than once" % name)
723
+ _check(
724
+ not self.autoscheduler().name,
725
+ f"The GeneratorParam {name} cannot be set more than once",
726
+ )
686
727
  self.autoscheduler().name = value
687
728
  elif name.startswith("autoscheduler."):
688
- sub_key = name[len("autoscheduler."):]
689
- _check(sub_key not in self.autoscheduler().extra,
690
- "The GeneratorParam %s cannot be set more than once" % name)
729
+ sub_key = name[len("autoscheduler.") :]
730
+ _check(
731
+ sub_key not in self.autoscheduler().extra,
732
+ f"The GeneratorParam {name} cannot be set more than once",
733
+ )
691
734
  self.autoscheduler().extra[sub_key] = value
692
735
  else:
693
736
  self._unhandled_generator_params[name] = value
@@ -706,17 +749,21 @@ class Generator(ABC):
706
749
  with self.context():
707
750
  self.generate()
708
751
 
709
- self._input_parameters = {n: getattr(self, n).parameter() for n in self._inputs_dict}
752
+ self._input_parameters = {
753
+ n: getattr(self, n).parameter() for n in self._inputs_dict
754
+ }
710
755
  self._output_funcs = {n: getattr(self, n) for n in self._outputs_dict}
711
756
  _check(
712
757
  len(self._output_funcs) > 0,
713
- "Generator %s must declare at least one Output in Arguments." % self._get_name(),
758
+ f"Generator {self._get_name()} must declare at least one Output in Arguments.",
714
759
  )
715
760
 
716
761
  funcs = []
717
762
  for name, f in self._output_funcs.items():
718
- _check(f.defined(), "Output '%s' was not defined." % name)
719
- self._requirements[name]._check_types_and_dimensions(f.types(), f.dimensions())
763
+ _check(f.defined(), f"Output '{name}' was not defined.")
764
+ self._requirements[name]._check_types_and_dimensions(
765
+ f.types(), f.dimensions()
766
+ )
720
767
  funcs.append(f)
721
768
 
722
769
  self._pipeline = Pipeline(funcs)
@@ -725,23 +772,25 @@ class Generator(ABC):
725
772
  self._stage = _Stage.pipeline_built
726
773
  return self._pipeline
727
774
 
728
- def _get_input_parameter(self, name: str) -> InternalParameter:
775
+ def _get_input_parameter(self, name: str):
729
776
  assert self._stage == _Stage.pipeline_built
730
- _check(name in self._input_parameters, "Unknown input: %s" % name)
777
+ _check(name in self._input_parameters, f"Unknown input: {name}")
731
778
  return self._input_parameters[name]
732
779
 
733
780
  def _get_output_func(self, name: str) -> Func:
734
781
  assert self._stage == _Stage.pipeline_built
735
- _check(name in self._output_funcs, "Unknown output: %s" % name)
782
+ _check(name in self._output_funcs, f"Unknown output: {name}")
736
783
  return self._output_funcs[name]
737
784
 
738
785
  def _bind_input(self, name: str, values: list[object]):
739
786
  assert self._stage < _Stage.io_replaced
740
787
  self._advance_to_stage(_Stage.configure_called)
741
- _check(len(values) == 1, "Too many values specified for input: %s" % name)
742
- _check(name in self._inputs_dict, "There is no input with the name: %s" % name)
788
+ _check(len(values) == 1, f"Too many values specified for input: {name}")
789
+ _check(name in self._inputs_dict, f"There is no input with the name: {name}")
743
790
  assert name not in self._replacements
744
- self._replacements[name] = self._inputs_dict[name]._make_replacement(values[0], self._requirements[name])
791
+ self._replacements[name] = self._inputs_dict[name]._make_replacement(
792
+ values[0], self._requirements[name]
793
+ )
745
794
 
746
795
 
747
796
  _python_generators: dict = {}
@@ -752,7 +801,7 @@ def _get_python_generator_names() -> list[str]:
752
801
 
753
802
 
754
803
  def _create_python_generator(name: str, context: GeneratorContext):
755
- cls = _python_generators.get(name, None)
804
+ cls = _python_generators.get(name)
756
805
  if not isclass(cls):
757
806
  return None
758
807
  with context:
@@ -775,10 +824,10 @@ def active_generator_context() -> GeneratorContext:
775
824
 
776
825
 
777
826
  def _is_interactive_mode() -> bool:
778
- return hasattr(sys, 'ps1')
827
+ return hasattr(sys, "ps1")
779
828
 
780
829
 
781
- def _check_generator_name_in_use(n:str):
830
+ def _check_generator_name_in_use(n: str):
782
831
  if _is_interactive_mode():
783
832
  # In interactive mode, it's OK to redefine generators... in fact, it's really
784
833
  # annoying not to allow this (e.g. in Colab)
@@ -788,27 +837,38 @@ def _check_generator_name_in_use(n:str):
788
837
  # builtins.print("REDEFINING ", n)
789
838
  return
790
839
 
791
- _check(n not in _python_generators, "The Generator name %s is already in use." % n)
840
+ _check(n not in _python_generators, f"The Generator name {n} is already in use.")
792
841
 
793
842
 
794
843
  def alias(**kwargs):
795
-
796
844
  def alias_impl(cls):
797
845
  for k, v in kwargs.items():
798
846
  _check_valid_name(k)
799
- _check(hasattr(cls, "_halide_registered_name"), "@alias can only be used in conjunction with @generator.")
847
+ _check(
848
+ hasattr(cls, "_halide_registered_name"),
849
+ "@alias can only be used in conjunction with @generator.",
850
+ )
800
851
  _check_generator_name_in_use(k)
801
- _check(type(v) is dict, "The Generator alias %s specifies something other than a dict." % k)
802
- new_cls = type(k, (cls,), {"_halide_registered_name": k, "_halide_alias_generator_params": v})
852
+ _check(
853
+ type(v) is dict,
854
+ f"The Generator alias {k} specifies something other than a dict.",
855
+ )
856
+ new_cls = type(
857
+ k,
858
+ (cls,),
859
+ {"_halide_registered_name": k, "_halide_alias_generator_params": v},
860
+ )
803
861
  _python_generators[k] = new_cls
804
862
  return cls
805
863
 
806
864
  return alias_impl
807
865
 
808
- def generator(name:str=""):
866
+
867
+ def generator(name: str = ""):
809
868
  # This code relies on dicts preserving key-insertion order, which is only
810
869
  # guaranteed for all Python implementations as of v3.7.
811
870
  _check(sys.version_info >= (3, 7), "Halide Generators require Python 3.7 or later.")
871
+
812
872
  def generator_impl(cls):
813
873
  n = name if name else _fqname(cls)
814
874
  _check_generator_name_in_use(n)
@@ -819,17 +879,20 @@ def generator(name:str=""):
819
879
  if issubclass(cls, Generator):
820
880
  new_cls = type(cls.__name__, (cls,), {"_halide_registered_name": n})
821
881
  else:
822
- new_cls = type(cls.__name__, (cls, Generator), {"_halide_registered_name": n})
882
+ new_cls = type(
883
+ cls.__name__, (cls, Generator), {"_halide_registered_name": n}
884
+ )
823
885
  _python_generators[n] = new_cls
824
886
  return new_cls
825
887
 
826
888
  return generator_impl
827
889
 
828
- def funcs(names:str) -> tuple(Func):
890
+
891
+ def funcs(names: str) -> tuple[Func, ...]:
829
892
  """Given a space-delimited string, create a Func for each substring and return as a tuple."""
830
- return (Func(n) for n in names.split(' '))
893
+ return tuple(Func(n) for n in names.split(" "))
831
894
 
832
895
 
833
- def vars(names:str) -> tuple(Var):
896
+ def vars(names: str) -> tuple[Var, ...]:
834
897
  """Given a space-delimited string, create a Var for each substring and return as a tuple."""
835
- return (Var(n) for n in names.split(' '))
898
+ return tuple(Var(n) for n in names.split(" "))