islpy 2025.1.5__cp313-cp313-macosx_11_0_arm64.whl → 2025.2__cp313-cp313-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.
islpy/_monkeypatch.py ADDED
@@ -0,0 +1,1015 @@
1
+ from collections.abc import Callable, Collection, Iterable, Mapping, Sequence
2
+ from typing import (
3
+ TYPE_CHECKING,
4
+ Any,
5
+ ClassVar,
6
+ Literal,
7
+ Protocol,
8
+ TypeAlias,
9
+ TypeVar,
10
+ cast,
11
+ )
12
+
13
+
14
+ if TYPE_CHECKING:
15
+ import islpy._isl as _isl
16
+ else:
17
+ import sys
18
+ if "_isl" not in sys.modules:
19
+ import islpy._isl as _isl
20
+ else:
21
+ # This is used for monkeypatching during stub generation.
22
+ # See stubgen/stubgen.py and CMakeLists for orchestration details.
23
+ import _isl
24
+
25
+
26
+ ALL_CLASSES: tuple[type, ...] = tuple(
27
+ getattr(_isl, cls) for cls in dir(_isl) if cls[0].isupper())
28
+ EXPR_CLASSES: tuple[type, ...] = tuple(cls for cls in ALL_CLASSES
29
+ if "Aff" in cls.__name__ or "Polynomial" in cls.__name__)
30
+ ARITH_CLASSES: tuple[type, ...] = (
31
+ _isl.Aff, _isl.PwAff, _isl.QPolynomial, _isl.PwQPolynomial)
32
+
33
+ _CHECK_DIM_TYPES: tuple[_isl.dim_type, ...] = (
34
+ _isl.dim_type.in_, _isl.dim_type.param, _isl.dim_type.set)
35
+
36
+
37
+ # {{{ typing helpers
38
+
39
+ SelfT = TypeVar("SelfT")
40
+
41
+ BasicT = TypeVar("BasicT", _isl.BasicSet, _isl.BasicMap)
42
+
43
+ AffOrConstraintT = TypeVar("AffOrConstraintT", _isl.Aff, _isl.Constraint)
44
+ AffLikeT = TypeVar("AffLikeT", _isl.Aff, _isl.PwAff)
45
+ ExprLike: TypeAlias = _isl.Aff | _isl.PwAff | _isl.QPolynomial | _isl.PwQPolynomial
46
+ ExprLikeT = TypeVar("ExprLikeT", _isl.Aff, _isl.PwAff,
47
+ _isl.QPolynomial, _isl.PwQPolynomial
48
+ )
49
+ SetLikeT = TypeVar("SetLikeT", bound=_isl.BasicSet | _isl.Set)
50
+ SetOrMap: TypeAlias = _isl.BasicSet | _isl.Set | _isl.BasicMap | _isl.Map
51
+ SetOrMapT = TypeVar("SetOrMapT", bound=SetOrMap)
52
+
53
+ HasSpace: TypeAlias = (
54
+ _isl.Space
55
+ | _isl.Aff
56
+ | _isl.BasicMap
57
+ | _isl.BasicSet
58
+ | _isl.Constraint
59
+ | _isl.LocalSpace
60
+ | _isl.Map
61
+ | _isl.MultiAff
62
+ | _isl.MultiId
63
+ | _isl.MultiPwAff
64
+ | _isl.MultiUnionPwAff
65
+ | _isl.MultiVal
66
+ | _isl.Point
67
+ | _isl.PwAff
68
+ | _isl.PwMultiAff
69
+ | _isl.PwQPolynomial
70
+ | _isl.PwQPolynomialFold
71
+ | _isl.QPolynomial
72
+ | _isl.QPolynomialFold
73
+ | _isl.Set
74
+ | _isl.UnionMap
75
+ | _isl.UnionPwAff
76
+ | _isl.UnionPwMultiAff
77
+ | _isl.UnionPwQPolynomial
78
+ | _isl.UnionPwQPolynomialFold
79
+ | _isl.UnionSet
80
+ )
81
+
82
+
83
+ class IslObject(Protocol):
84
+ def get_ctx(self) -> _isl.Context:
85
+ ...
86
+
87
+ def _wraps_same_instance_as(self, other: object) -> bool:
88
+ ...
89
+
90
+ _base_name: ClassVar[str]
91
+
92
+ # }}}
93
+
94
+
95
+ # {{{ copied verbatim from pytools to avoid numpy/pytools dependency
96
+
97
+ F = TypeVar("F", bound=Callable[..., Any])
98
+
99
+
100
+ class _HasKwargs:
101
+ pass
102
+
103
+
104
+ def _memoize_on_first_arg(function: F, cache_dict_name: str | None = None) -> F:
105
+ """Like :func:`memoize_method`, but for functions that take the object
106
+ in which do memoization information is stored as first argument.
107
+
108
+ Supports cache deletion via ``function_name.clear_cache(self)``.
109
+ """
110
+ from sys import intern
111
+
112
+ if cache_dict_name is None:
113
+ cache_dict_name = intern(
114
+ f"_memoize_dic_{function.__module__}{function.__name__}"
115
+ )
116
+
117
+ def wrapper(obj, *args, **kwargs):
118
+ if kwargs:
119
+ key = (_HasKwargs, frozenset(kwargs.items()), *args)
120
+ else:
121
+ key = args
122
+
123
+ try:
124
+ return getattr(obj, cache_dict_name)[key]
125
+ except AttributeError:
126
+ attribute_error = True
127
+ except KeyError:
128
+ attribute_error = False
129
+
130
+ result = function(obj, *args, **kwargs)
131
+ if attribute_error:
132
+ object.__setattr__(obj, cache_dict_name, {key: result})
133
+ return result
134
+ else:
135
+ getattr(obj, cache_dict_name)[key] = result
136
+ return result
137
+
138
+ def clear_cache(obj):
139
+ object.__delattr__(obj, cache_dict_name)
140
+
141
+ from functools import update_wrapper
142
+ new_wrapper = update_wrapper(wrapper, function)
143
+
144
+ # type-ignore because mypy has a point here, stuffing random attributes
145
+ # into the function's dict is moderately sketchy.
146
+ new_wrapper.clear_cache = clear_cache # type: ignore[attr-defined]
147
+
148
+ return cast("F", new_wrapper)
149
+
150
+ # }}}
151
+
152
+
153
+ def _read_from_str_wrapper(cls, context, s, dims_with_apostrophes):
154
+ """A callable to reconstitute instances from strings for the benefit
155
+ of Python's ``__reduce__`` protocol.
156
+ """
157
+ cls_from_str = cls.read_from_str(context, s)
158
+
159
+ # Apostrophes in dim names have been lost, put them back
160
+ for dim_name, (dt, dim_idx) in dims_with_apostrophes.items():
161
+ cls_from_str = cls_from_str.set_dim_name(dt, dim_idx, dim_name)
162
+
163
+ return cls_from_str
164
+
165
+
166
+ def dim_type_reduce(self: _isl.dim_type):
167
+ return (_isl.dim_type, (int(self),))
168
+
169
+
170
+ def context_reduce(self: _isl.Context):
171
+ from islpy import DEFAULT_CONTEXT, _get_default_context
172
+ if self._wraps_same_instance_as(DEFAULT_CONTEXT):
173
+ return (_get_default_context, ())
174
+ else:
175
+ return (_isl.Context, ())
176
+
177
+
178
+ def context_eq(self: IslObject, other: object):
179
+ return isinstance(other, _isl.Context) and self._wraps_same_instance_as(other)
180
+
181
+
182
+ def context_ne(self: object, other: object) -> bool:
183
+ return not self.__eq__(other)
184
+
185
+
186
+ def generic_reduce(self: IslObject):
187
+ ctx = self.get_ctx()
188
+ prn = _isl.Printer.to_str(ctx)
189
+ prn = getattr(prn, f"print_{self._base_name}")(self)
190
+
191
+ # Reconstructing from string will remove apostrophes in dim names,
192
+ # so keep track of dim names with apostrophes
193
+ dims_with_apostrophes = {
194
+ dname: pos for dname, pos in self.get_var_dict().items()
195
+ if "'" in dname}
196
+
197
+ return (
198
+ _read_from_str_wrapper,
199
+ (type(self), ctx, prn.get_str(), dims_with_apostrophes))
200
+
201
+
202
+ def generic_str(self: IslObject) -> str:
203
+ prn = _isl.Printer.to_str(self.get_ctx())
204
+ getattr(prn, f"print_{self._base_name}")(self)
205
+ return prn.get_str()
206
+
207
+
208
+ def generic_repr(self: IslObject) -> str:
209
+ prn = _isl.Printer.to_str(self.get_ctx())
210
+ getattr(prn, f"print_{self._base_name}")(self)
211
+ return f'{type(self).__name__}("{prn.get_str()}")'
212
+
213
+
214
+ def space_get_id_dict(
215
+ self: _isl.Space,
216
+ dimtype: _isl.dim_type | None = None
217
+ ) -> Mapping[_isl.Id, tuple[_isl.dim_type, int]]:
218
+ """Return a dictionary mapping variable :class:`Id` instances to tuples
219
+ of (:class:`dim_type`, index).
220
+
221
+ :param dimtype: None to get all variables, otherwise
222
+ one of :class:`dim_type`.
223
+ """
224
+ result = {}
225
+
226
+ def set_dim_id(name, tp, idx):
227
+ if name in result:
228
+ raise RuntimeError(f"non-unique var id '{name}' encountered")
229
+ result[name] = tp, idx
230
+
231
+ if dimtype is None:
232
+ types = _CHECK_DIM_TYPES
233
+ else:
234
+ types = [dimtype]
235
+
236
+ for tp in types:
237
+ for i in range(self.dim(tp)):
238
+ name = self.get_dim_id(tp, i)
239
+ if name is not None:
240
+ set_dim_id(name, tp, i)
241
+
242
+ return result
243
+
244
+
245
+ def space_get_var_dict(
246
+ self: _isl.Space,
247
+ dimtype: _isl.dim_type | None = None,
248
+ ignore_out: bool = False
249
+ ) -> Mapping[str, tuple[_isl.dim_type, int]]:
250
+ """Return a dictionary mapping variable names to tuples of
251
+ (:class:`dim_type`, index).
252
+
253
+ :param dimtype: None to get all variables, otherwise
254
+ one of :class:`dim_type`.
255
+ """
256
+ result: dict[str, tuple[_isl.dim_type, int]] = {}
257
+
258
+ def set_dim_name(name: str, tp: _isl.dim_type, idx: int):
259
+ if name in result:
260
+ raise RuntimeError(f"non-unique var name '{name}' encountered")
261
+ result[name] = tp, idx
262
+
263
+ if dimtype is None:
264
+ types = list(_CHECK_DIM_TYPES)
265
+ if ignore_out:
266
+ types = types[:]
267
+ types.remove(_isl.dim_type.out)
268
+ else:
269
+ types = [dimtype]
270
+
271
+ for tp in types:
272
+ for i in range(self.dim(tp)):
273
+ name = self.get_dim_name(tp, i)
274
+ if name is not None:
275
+ set_dim_name(name, tp, i)
276
+
277
+ return result
278
+
279
+
280
+ def space_create_from_names(
281
+ ctx: _isl.Context,
282
+ set: Sequence[str] | None = None,
283
+ in_: Sequence[str] | None = None,
284
+ out: Sequence[str] | None = None,
285
+ params: Sequence[str] = ()
286
+ ) -> _isl.Space:
287
+ """Create a :class:`Space` from lists of variable names.
288
+
289
+ :param set_: names of `set`-type variables.
290
+ :param in_: names of `in`-type variables.
291
+ :param out: names of `out`-type variables.
292
+ :param params: names of parameter-type variables.
293
+ """
294
+ dt = _isl.dim_type
295
+
296
+ if set is not None:
297
+ if in_ is not None or out is not None:
298
+ raise RuntimeError("must pass only one of set / (in_,out)")
299
+
300
+ result = _isl.Space.set_alloc(ctx, nparam=len(params),
301
+ dim=len(set))
302
+
303
+ for i, name in enumerate(set):
304
+ result = result.set_dim_name(dt.set, i, name)
305
+
306
+ elif in_ is not None and out is not None:
307
+ if set is not None:
308
+ raise RuntimeError("must pass only one of set / (in_,out)")
309
+
310
+ result = _isl.Space.alloc(ctx, nparam=len(params),
311
+ n_in=len(in_), n_out=len(out))
312
+
313
+ for i, name in enumerate(in_):
314
+ result = result.set_dim_name(dt.in_, i, name)
315
+
316
+ for i, name in enumerate(out):
317
+ result = result.set_dim_name(dt.out, i, name)
318
+ else:
319
+ raise RuntimeError("invalid parameter combination")
320
+
321
+ for i, name in enumerate(params):
322
+ result = result.set_dim_name(dt.param, i, name)
323
+
324
+ return result
325
+
326
+
327
+ def obj_or(self: SetOrMapT, other: object) -> SetOrMapT:
328
+ try:
329
+ return self.union(other)
330
+ except TypeError:
331
+ return NotImplemented
332
+
333
+
334
+ def obj_and(self: SetOrMapT, other: object) -> SetOrMapT:
335
+ try:
336
+ return self.intersect(other)
337
+ except TypeError:
338
+ return NotImplemented
339
+
340
+
341
+ def obj_sub(self: SetOrMapT, other: object) -> SetOrMapT:
342
+ try:
343
+ return self.subtract(other)
344
+ except TypeError:
345
+ return NotImplemented
346
+
347
+
348
+ def obj_set_coefficients(
349
+ self: AffOrConstraintT,
350
+ dim_tp: _isl.dim_type,
351
+ args: Sequence[_isl.Val | int],
352
+ ) -> AffOrConstraintT:
353
+ """
354
+ :param dim_tp: :class:`dim_type`
355
+ :param args: :class:`list` of coefficients, for indices `0..len(args)-1`.
356
+
357
+ .. versionchanged:: 2011.3
358
+ New for :class:`Aff`
359
+ """
360
+ for i, coeff in enumerate(args):
361
+ self = self.set_coefficient_val(dim_tp, i, coeff)
362
+
363
+ return self
364
+
365
+
366
+ def obj_set_coefficients_by_name(
367
+ self: AffOrConstraintT,
368
+ iterable: Iterable[tuple[str | Literal[1], _isl.Val | int]]
369
+ | Mapping[str | Literal[1], _isl.Val | int],
370
+ name_to_dim: Mapping[str, tuple[_isl.dim_type, int]] | None = None,
371
+ ) -> AffOrConstraintT:
372
+ """Set the coefficients and the constant.
373
+
374
+ :param iterable: a :class:`dict` or iterable of :class:`tuple`
375
+ instances mapping variable names to their coefficients.
376
+ The constant is set to the value of the key '1'.
377
+
378
+ .. versionchanged:: 2011.3
379
+ New for :class:`Aff`
380
+ """
381
+ try:
382
+ coeff_iterable: Iterable[tuple[str | Literal[1], _isl.Val | int]] = \
383
+ list(iterable.items())
384
+ except AttributeError:
385
+ coeff_iterable = \
386
+ cast("Iterable[tuple[str | Literal[1], _isl.Val | int]]", iterable)
387
+
388
+ if name_to_dim is None:
389
+ name_to_dim = obj_get_var_dict(self)
390
+
391
+ for name, coeff in coeff_iterable:
392
+ if name == 1:
393
+ self = self.set_constant_val(coeff)
394
+ else:
395
+ assert name
396
+ tp, idx = name_to_dim[name]
397
+ self = self.set_coefficient_val(tp, idx, coeff)
398
+
399
+ return self
400
+
401
+
402
+ def obj_get_coefficients_by_name(
403
+ self: _isl.Constraint | _isl.Aff,
404
+ dimtype: _isl.dim_type | None = None,
405
+ dim_to_name: Mapping[tuple[_isl.dim_type, int], str] | None = None,
406
+ ) -> dict[str | Literal[1], _isl.Val]:
407
+ """Return a dictionary mapping variable names to coefficients.
408
+
409
+ :param dimtype: None to get all variables, otherwise
410
+ one of :class:`dim_type`.
411
+
412
+ .. versionchanged:: 2011.3
413
+ New for :class:`Aff`
414
+ """
415
+ if dimtype is None:
416
+ types: Sequence[_isl.dim_type] = _CHECK_DIM_TYPES
417
+ else:
418
+ types = [dimtype]
419
+
420
+ result: dict[Literal[1] | str, _isl.Val] = {}
421
+ for tp in types:
422
+ for i in range(self.get_space().dim(tp)):
423
+ coeff = self.get_coefficient_val(tp, i)
424
+ if coeff:
425
+ if dim_to_name is None:
426
+ name = self.get_dim_name(tp, i)
427
+ assert name
428
+ else:
429
+ name = dim_to_name[tp, i]
430
+
431
+ result[name] = coeff
432
+
433
+ const = self.get_constant_val()
434
+ if const:
435
+ result[1] = const
436
+
437
+ return result
438
+
439
+
440
+ def eq_from_names(
441
+ space: _isl.Space,
442
+ coefficients: Mapping[str | Literal[1], _isl.Val | int] | None = None
443
+ ) -> _isl.Constraint:
444
+ """Create a constraint `const + coeff_1*var_1 +... == 0`.
445
+
446
+ :param space: :class:`Space`
447
+ :param coefficients: a :class:`dict` or iterable of :class:`tuple`
448
+ instances mapping variable names to their coefficients
449
+ The constant is set to the value of the key '1'.
450
+
451
+ .. versionchanged:: 2011.3
452
+ Eliminated the separate *const* parameter.
453
+ """
454
+ if coefficients is None:
455
+ coefficients = {}
456
+ c = _isl.Constraint.equality_alloc(space)
457
+ return obj_set_coefficients_by_name(c, coefficients)
458
+
459
+
460
+ def ineq_from_names(
461
+ space: _isl.Space,
462
+ coefficients: Mapping[str | Literal[1], _isl.Val | int] | None = None
463
+ ) -> _isl.Constraint:
464
+ """Create a constraint `const + coeff_1*var_1 +... >= 0`.
465
+
466
+ :param space: :class:`Space`
467
+ :param coefficients: a :class:`dict` or iterable of :class:`tuple`
468
+ instances mapping variable names to their coefficients
469
+ The constant is set to the value of the key '1'.
470
+
471
+ .. versionchanged:: 2011.3
472
+ Eliminated the separate *const* parameter.
473
+ """
474
+ if coefficients is None:
475
+ coefficients = {}
476
+ c = _isl.Constraint.inequality_alloc(space)
477
+ return obj_set_coefficients_by_name(c, coefficients)
478
+
479
+
480
+ def basic_obj_get_constraints(
481
+ self: _isl.BasicSet | _isl.BasicMap
482
+ ) -> list[_isl.Constraint]:
483
+ """Get a list of constraints."""
484
+ result: list[_isl.Constraint] = []
485
+ self.foreach_constraint(result.append)
486
+ return result
487
+
488
+
489
+ def set_get_basic_sets(self: _isl.Set | _isl.BasicSet) -> list[_isl.BasicSet]:
490
+ """Get the list of :class:`BasicSet` instances in this :class:`Set`."""
491
+ result: list[_isl.BasicSet] = []
492
+ self.foreach_basic_set(result.append)
493
+ return result
494
+
495
+
496
+ def map_get_basic_maps(self: _isl.Map) -> list[_isl.BasicMap]:
497
+ """Get the list of :class:`BasicMap` instances in this :class:`Map`."""
498
+ result: list[_isl.BasicMap] = []
499
+ self.foreach_basic_map(result.append)
500
+ return result
501
+
502
+
503
+ def obj_get_id_dict(
504
+ self: HasSpace,
505
+ dimtype: _isl.dim_type | None = None
506
+ ) -> Mapping[_isl.Id, tuple[_isl.dim_type, int]]:
507
+ """Return a dictionary mapping :class:`Id` instances to tuples of
508
+ (:class:`dim_type`, index).
509
+
510
+ :param dimtype: None to get all variables, otherwise
511
+ one of :class:`dim_type`.
512
+ """
513
+ return self.get_space().get_id_dict(dimtype)
514
+
515
+
516
+ @_memoize_on_first_arg
517
+ def obj_get_var_dict(
518
+ self: HasSpace,
519
+ dimtype: _isl.dim_type | None = None
520
+ ) -> Mapping[str, tuple[_isl.dim_type, int]]:
521
+ """Return a dictionary mapping variable names to tuples of
522
+ (:class:`dim_type`, index).
523
+
524
+ :param dimtype: None to get all variables, otherwise
525
+ one of :class:`dim_type`.
526
+ """
527
+ return self.get_space().get_var_dict(
528
+ dimtype, ignore_out=isinstance(self, EXPR_CLASSES))
529
+
530
+
531
+ def obj_get_var_ids(
532
+ self: HasSpace,
533
+ dimtype: _isl.dim_type
534
+ ) -> Sequence[str]:
535
+ """Return a list of :class:`Id` instances for :class:`dim_type` *dimtype*."""
536
+ return [self.get_dim_name(dimtype, i) for i in range(self.dim(dimtype))]
537
+
538
+
539
+ @_memoize_on_first_arg
540
+ def obj_get_var_names(self: HasSpace, dimtype: _isl.dim_type) -> Sequence[str]:
541
+ """Return a list of dim names (in order) for :class:`dim_type` *dimtype*."""
542
+ return [self.get_dim_name(dimtype, i)
543
+ for i in range(self.dim(dimtype))]
544
+
545
+
546
+ def pwaff_get_pieces(self: _isl.PwAff | _isl.Aff) -> list[tuple[_isl.Set, _isl.Aff]]:
547
+ result: list[tuple[_isl.Set, _isl.Aff]] = []
548
+
549
+ def append_tuple(s: _isl.Set, v: _isl.Aff):
550
+ result.append((s, v))
551
+
552
+ self.foreach_piece(append_tuple)
553
+ return result
554
+
555
+
556
+ def pwqpolynomial_get_pieces(
557
+ self: _isl.PwQPolynomial
558
+ ) -> list[tuple[_isl.Set, _isl.QPolynomial]]:
559
+ """
560
+ :return: list of (:class:`Set`, :class:`QPolynomial`)
561
+ """
562
+
563
+ result: list[tuple[_isl.Set, _isl.QPolynomial]] = []
564
+
565
+ def append_tuple(s: _isl.Set, v: _isl.QPolynomial):
566
+ result.append((s, v))
567
+
568
+ self.foreach_piece(append_tuple)
569
+ return result
570
+
571
+
572
+ def pw_get_aggregate_domain(self: _isl.PwAff | _isl.PwQPolynomial) -> _isl.Set:
573
+ """
574
+ :return: a :class:`Set` that is the union of the domains of all pieces
575
+ """
576
+
577
+ result = _isl.Set.empty(self.get_domain_space())
578
+ for dom, _ in self.get_pieces():
579
+ result = result.union(cast("_isl.Set", dom))
580
+
581
+ return result
582
+
583
+
584
+ def qpolynomial_get_terms(self: _isl.QPolynomial) -> list[_isl.Term]:
585
+ """Get the list of :class:`Term` instances in this :class:`QPolynomial`."""
586
+ result: list[_isl.Term] = []
587
+ self.foreach_term(result.append)
588
+ return result
589
+
590
+
591
+ def pwqpolynomial_eval_with_dict(
592
+ self: _isl.PwQPolynomial,
593
+ value_dict: Mapping[str, int | _isl.Val]
594
+ ) -> int:
595
+ """Evaluates *self* for the parameters specified by
596
+ *value_dict*, which maps parameter names to their values.
597
+ """
598
+
599
+ pt = _isl.Point.zero(self.space.params())
600
+
601
+ for i in range(self.space.dim(_isl.dim_type.param)):
602
+ par_name = self.space.get_dim_name(_isl.dim_type.param, i)
603
+ assert par_name
604
+ pt = pt.set_coordinate_val(
605
+ _isl.dim_type.param, i, value_dict[par_name])
606
+
607
+ return self.eval(pt).to_python()
608
+
609
+
610
+ def _number_to_expr_like(template: ExprLikeT, num: int | _isl.Val) -> ExprLikeT:
611
+ number_aff = _isl.Aff.zero_on_domain(template.get_domain_space())
612
+ number_aff = number_aff.set_constant_val(num)
613
+
614
+ if isinstance(template, _isl.Aff):
615
+ return number_aff
616
+ if isinstance(template, _isl.QPolynomial):
617
+ return _isl.QPolynomial.from_aff(number_aff)
618
+
619
+ # everything else is piecewise
620
+
621
+ if template.get_pieces():
622
+ number_pw_aff = _isl.PwAff.empty(template.get_space())
623
+ for set, _ in template.get_pieces():
624
+ number_pw_aff = set.indicator_function().cond(
625
+ number_aff, number_pw_aff)
626
+ else:
627
+ number_pw_aff = _isl.PwAff.alloc(
628
+ _isl.Set.universe(template.domain().space),
629
+ number_aff)
630
+
631
+ if isinstance(template, _isl.PwAff):
632
+ return number_pw_aff
633
+
634
+ elif isinstance(template, _isl.PwQPolynomial):
635
+ return _isl.PwQPolynomial.from_pw_aff(number_pw_aff)
636
+
637
+ else:
638
+ raise TypeError("unexpected template type")
639
+
640
+
641
+ def expr_like_add(self: ExprLikeT, other: ExprLikeT | int | _isl.Val) -> ExprLikeT:
642
+ if not isinstance(other, ExprLike):
643
+ other = _number_to_expr_like(self, other)
644
+
645
+ try:
646
+ return self.add(other)
647
+ except TypeError:
648
+ return NotImplemented
649
+
650
+
651
+ def expr_like_sub(self: ExprLikeT, other: ExprLikeT | int | _isl.Val):
652
+ if not isinstance(other, ExprLike):
653
+ other = _number_to_expr_like(self, other)
654
+
655
+ try:
656
+ return self.sub(other)
657
+ except TypeError:
658
+ return NotImplemented
659
+
660
+
661
+ def expr_like_rsub(self: ExprLikeT, other: ExprLikeT | int | _isl.Val) -> ExprLikeT:
662
+ if not isinstance(other, ExprLike):
663
+ other = _number_to_expr_like(self, other)
664
+
665
+ return -self + other
666
+
667
+
668
+ def expr_like_mul(self: ExprLikeT, other: ExprLikeT | int | _isl.Val) -> ExprLikeT:
669
+ if not isinstance(other, ExprLike):
670
+ other = _number_to_expr_like(self, other)
671
+
672
+ try:
673
+ return self.mul(other)
674
+ except TypeError:
675
+ return NotImplemented
676
+
677
+
678
+ def expr_like_floordiv(self: AffLikeT, other: _isl.Val) -> AffLikeT:
679
+ return self.scale_down_val(other).floor()
680
+
681
+
682
+ def val_rsub(self: _isl.Val, other: _isl.Val) -> _isl.Val:
683
+ return -self + other
684
+
685
+
686
+ def val_bool(self: _isl.Val) -> bool:
687
+ return not self.is_zero()
688
+
689
+
690
+ def val_repr(self: _isl.Val) -> str:
691
+ return f'{type(self).__name__}("{self.to_str()}")'
692
+
693
+
694
+ def val_to_python(self: _isl.Val) -> int:
695
+ if not self.is_int():
696
+ raise ValueError("can only convert integer Val to python")
697
+
698
+ return int(self.to_str())
699
+
700
+
701
+ def obj_eq(self: IslObject, other: object) -> bool:
702
+ assert self.get_ctx() == other.get_ctx(), (
703
+ "Equality-comparing two objects from different ISL Contexts "
704
+ "will likely lead to entertaining (but never useful) results. "
705
+ "In particular, Spaces with matching names will no longer be "
706
+ "equal.")
707
+
708
+ return self.is_equal(other)
709
+
710
+
711
+ def obj_ne(self: object, other: object) -> bool:
712
+ return not self.__eq__(other)
713
+
714
+
715
+ for cls in ALL_CLASSES:
716
+ if hasattr(cls, "is_equal"):
717
+ cls.__eq__ = obj_eq
718
+ cls.__ne__ = obj_ne
719
+
720
+
721
+ def obj_lt(self: SetOrMapT, other: SetOrMapT) -> bool:
722
+ return self.is_strict_subset(other)
723
+
724
+
725
+ def obj_le(self: SetOrMapT, other: SetOrMapT) -> bool:
726
+ return self.is_subset(other)
727
+
728
+
729
+ def obj_gt(self: SetOrMapT, other: SetOrMapT) -> bool:
730
+ return other.is_strict_subset(self)
731
+
732
+
733
+ def obj_ge(self: SetOrMapT, other: SetOrMapT) -> bool:
734
+ return other.is_subset(self)
735
+
736
+
737
+ # {{{ project_out_except
738
+
739
+ def obj_project_out_except(
740
+ obj: SetLikeT,
741
+ names: Collection[str],
742
+ types: Collection[_isl.dim_type]
743
+ ) -> SetLikeT:
744
+ """
745
+ :param types: list of :class:`dim_type` determining
746
+ the types of axes to project out
747
+ :param names: names of axes matching the above which
748
+ should be left alone by the projection
749
+
750
+ .. versionadded:: 2011.3
751
+ """
752
+
753
+ for tp in types:
754
+ while True:
755
+ space = obj.get_space()
756
+ var_dict = space.get_var_dict(tp)
757
+
758
+ all_indices = set(range(space.dim(tp)))
759
+ leftover_indices = {var_dict[name][1] for name in names
760
+ if name in var_dict}
761
+ project_indices = all_indices-leftover_indices
762
+ if not project_indices:
763
+ break
764
+
765
+ min_index = min(project_indices)
766
+ count = 1
767
+ while min_index+count in project_indices:
768
+ count += 1
769
+
770
+ obj = obj.project_out(tp, min_index, count)
771
+
772
+ return obj
773
+
774
+ # }}}
775
+
776
+
777
+ # {{{ eliminate_except
778
+
779
+ def obj_eliminate_except(
780
+ obj: SetLikeT,
781
+ names: Collection[str],
782
+ types: Collection[_isl.dim_type]
783
+ ) -> SetLikeT:
784
+ """
785
+ :param types: list of :class:`dim_type` determining
786
+ the types of axes to eliminate
787
+ :param names: names of axes matching the above which
788
+ should be left alone by the eliminate
789
+
790
+ .. versionadded:: 2011.3
791
+ """
792
+
793
+ for tp in types:
794
+ space = obj.get_space()
795
+ var_dict = space.get_var_dict(tp)
796
+ to_eliminate = (
797
+ set(range(space.dim(tp)))
798
+ - {var_dict[name][1] for name in names
799
+ if name in var_dict})
800
+
801
+ while to_eliminate:
802
+ min_index = min(to_eliminate)
803
+ count = 1
804
+ while min_index+count in to_eliminate:
805
+ count += 1
806
+
807
+ obj = obj.eliminate(tp, min_index, count)
808
+
809
+ to_eliminate -= set(range(min_index, min_index+count))
810
+
811
+ return obj
812
+
813
+ # }}}
814
+
815
+
816
+ # {{{ add_constraints
817
+
818
+ def obj_add_constraints(obj: BasicT, constraints: Iterable[_isl.Constraint]) -> BasicT:
819
+ """
820
+ .. versionadded:: 2011.3
821
+ """
822
+
823
+ for cns in constraints:
824
+ obj = obj.add_constraint(cns)
825
+
826
+ return obj
827
+
828
+ # }}}
829
+
830
+
831
+ def _add_functionality() -> None:
832
+ _isl.dim_type.__reduce__ = dim_type_reduce
833
+
834
+ # {{{ Context
835
+
836
+ _isl.Context.__reduce__ = context_reduce
837
+ _isl.Context.__eq__ = context_eq
838
+ _isl.Context.__ne__ = context_ne
839
+
840
+ # }}}
841
+
842
+ # {{{ generic initialization, pickling
843
+
844
+ for cls in ALL_CLASSES:
845
+ if hasattr(cls, "read_from_str"):
846
+ cls.__reduce__ = generic_reduce
847
+
848
+ # }}}
849
+
850
+ # {{{ printing
851
+
852
+ for cls in ALL_CLASSES:
853
+ if (hasattr(cls, "_base_name")
854
+ and hasattr(_isl.Printer, f"print_{cls._base_name}")):
855
+ cls.__str__ = generic_str
856
+ cls.__repr__ = generic_repr
857
+
858
+ if not hasattr(cls, "__hash__"):
859
+ raise AssertionError(f"not hashable: {cls}")
860
+
861
+ # }}}
862
+
863
+ # {{{ Python set-like behavior
864
+
865
+ for cls in [_isl.BasicSet, _isl.BasicMap, _isl.Set, _isl.Map]:
866
+ cls.__or__ = obj_or
867
+ cls.__ror__ = obj_or
868
+ cls.__and__ = obj_and
869
+ cls.__rand__ = obj_and
870
+ cls.__sub__ = obj_sub
871
+
872
+ # }}}
873
+
874
+ # {{{ Space
875
+
876
+ _isl.Space.create_from_names = staticmethod(space_create_from_names)
877
+ _isl.Space.get_var_dict = space_get_var_dict
878
+ _isl.Space.get_id_dict = space_get_id_dict
879
+
880
+ # }}}
881
+
882
+ # {{{ coefficient wrangling
883
+
884
+ for coeff_class in [_isl.Constraint, _isl.Aff]:
885
+ coeff_class.set_coefficients = obj_set_coefficients
886
+ coeff_class.set_coefficients_by_name = obj_set_coefficients_by_name
887
+ coeff_class.get_coefficients_by_name = obj_get_coefficients_by_name
888
+
889
+ # }}}
890
+
891
+ # {{{ Constraint
892
+
893
+ _isl.Constraint.eq_from_names = staticmethod(eq_from_names)
894
+ _isl.Constraint.ineq_from_names = staticmethod(ineq_from_names)
895
+
896
+ # }}}
897
+
898
+ # {{{ BasicSet
899
+
900
+ _isl.BasicSet.get_constraints = basic_obj_get_constraints
901
+
902
+ # }}}
903
+
904
+ # {{{ BasicMap
905
+
906
+ _isl.BasicMap.get_constraints = basic_obj_get_constraints
907
+
908
+ # }}}
909
+
910
+ # {{{ Set
911
+
912
+ _isl.Set.get_basic_sets = set_get_basic_sets
913
+ _isl.BasicSet.get_basic_sets = set_get_basic_sets
914
+
915
+ # }}}
916
+
917
+ # {{{ Map
918
+
919
+ _isl.Map.get_basic_maps = map_get_basic_maps
920
+
921
+ # }}}
922
+
923
+
924
+ # {{{ common functionality
925
+
926
+ for cls in ALL_CLASSES:
927
+ if hasattr(cls, "get_space") and cls is not _isl.Space:
928
+ cls.get_id_dict = obj_get_id_dict
929
+ cls.get_var_dict = obj_get_var_dict
930
+ cls.get_var_ids = obj_get_var_ids
931
+ cls.get_var_names = obj_get_var_names
932
+
933
+ # }}}
934
+
935
+ # {{{ piecewise
936
+
937
+ _isl.PwAff.get_pieces = pwaff_get_pieces
938
+ _isl.Aff.get_pieces = pwaff_get_pieces
939
+ _isl.PwAff.get_aggregate_domain = pw_get_aggregate_domain
940
+
941
+ _isl.PwQPolynomial.get_pieces = pwqpolynomial_get_pieces
942
+ _isl.PwQPolynomial.get_aggregate_domain = pw_get_aggregate_domain
943
+
944
+ # }}}
945
+
946
+ _isl.QPolynomial.get_terms = qpolynomial_get_terms
947
+
948
+ _isl.PwQPolynomial.eval_with_dict = pwqpolynomial_eval_with_dict
949
+
950
+ # {{{ arithmetic
951
+
952
+ for expr_like_class in ARITH_CLASSES:
953
+ expr_like_class.__add__ = expr_like_add
954
+ expr_like_class.__radd__ = expr_like_add
955
+ expr_like_class.__sub__ = expr_like_sub
956
+ expr_like_class.__rsub__ = expr_like_rsub
957
+ expr_like_class.__mul__ = expr_like_mul
958
+ expr_like_class.__rmul__ = expr_like_mul
959
+ expr_like_class.__neg__ = expr_like_class.neg
960
+
961
+ for qpoly_class in [_isl.QPolynomial, _isl.PwQPolynomial]:
962
+ qpoly_class.__pow__ = qpoly_class.pow
963
+
964
+ for aff_class in [_isl.Aff, _isl.PwAff]:
965
+ aff_class.__mod__ = aff_class.mod_val
966
+ aff_class.__floordiv__ = expr_like_floordiv
967
+
968
+ # }}}
969
+
970
+ # {{{ Val
971
+
972
+ val_cls = _isl.Val
973
+
974
+ val_cls.__add__ = val_cls.add
975
+ val_cls.__radd__ = val_cls.add
976
+ val_cls.__sub__ = val_cls.sub
977
+ val_cls.__rsub__ = val_rsub
978
+ val_cls.__mul__ = val_cls.mul
979
+ val_cls.__rmul__ = val_cls.mul
980
+ val_cls.__neg__ = val_cls.neg
981
+ val_cls.__mod__ = val_cls.mod
982
+ val_cls.__bool__ = val_cls.__nonzero__ = val_bool
983
+
984
+ val_cls.__lt__ = val_cls.lt
985
+ val_cls.__gt__ = val_cls.gt
986
+ val_cls.__le__ = val_cls.le
987
+ val_cls.__ge__ = val_cls.ge
988
+ val_cls.__eq__ = val_cls.eq
989
+ val_cls.__ne__ = val_cls.ne
990
+
991
+ val_cls.__repr__ = val_repr
992
+ val_cls.__str__ = val_cls.to_str
993
+ val_cls.to_python = val_to_python
994
+
995
+ # }}}
996
+
997
+ # {{{ rich comparisons
998
+
999
+ for cls in [_isl.BasicSet, _isl.BasicMap, _isl.Set, _isl.Map]:
1000
+ cls.__lt__ = obj_lt
1001
+ cls.__le__ = obj_le
1002
+ cls.__gt__ = obj_gt
1003
+ cls.__ge__ = obj_ge
1004
+
1005
+ # }}}
1006
+
1007
+ for c in [_isl.BasicSet, _isl.BasicMap, _isl.Set, _isl.Map]:
1008
+ c.project_out_except = obj_project_out_except
1009
+ c.add_constraints = obj_add_constraints
1010
+
1011
+ for c in [_isl.BasicSet, _isl.Set]:
1012
+ c.eliminate_except = obj_eliminate_except
1013
+
1014
+
1015
+ _add_functionality()