islpy 2025.1.3__cp310-cp310-musllinux_1_2_aarch64.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/__init__.py ADDED
@@ -0,0 +1,1271 @@
1
+ __copyright__ = "Copyright (C) 2011-20 Andreas Kloeckner"
2
+
3
+ __license__ = """
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in
12
+ all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ THE SOFTWARE.
21
+ """
22
+
23
+ from typing import Any, Callable, Optional, TypeVar, cast
24
+
25
+ import islpy._isl as _isl
26
+ from islpy.version import VERSION, VERSION_TEXT # noqa
27
+
28
+
29
+ __version__ = VERSION_TEXT
30
+
31
+ # {{{ copied verbatim from pytools to avoid numpy/pytools dependency
32
+
33
+ F = TypeVar("F", bound=Callable[..., Any])
34
+
35
+
36
+ class _HasKwargs:
37
+ pass
38
+
39
+
40
+ def _memoize_on_first_arg(function: F, cache_dict_name: Optional[str] = None) -> F:
41
+ """Like :func:`memoize_method`, but for functions that take the object
42
+ in which do memoization information is stored as first argument.
43
+
44
+ Supports cache deletion via ``function_name.clear_cache(self)``.
45
+ """
46
+ from sys import intern
47
+
48
+ if cache_dict_name is None:
49
+ cache_dict_name = intern(
50
+ f"_memoize_dic_{function.__module__}{function.__name__}"
51
+ )
52
+
53
+ def wrapper(obj, *args, **kwargs):
54
+ if kwargs:
55
+ key = (_HasKwargs, frozenset(kwargs.items()), *args)
56
+ else:
57
+ key = args
58
+
59
+ try:
60
+ return getattr(obj, cache_dict_name)[key]
61
+ except AttributeError:
62
+ attribute_error = True
63
+ except KeyError:
64
+ attribute_error = False
65
+
66
+ result = function(obj, *args, **kwargs)
67
+ if attribute_error:
68
+ object.__setattr__(obj, cache_dict_name, {key: result})
69
+ return result
70
+ else:
71
+ getattr(obj, cache_dict_name)[key] = result
72
+ return result
73
+
74
+ def clear_cache(obj):
75
+ object.__delattr__(obj, cache_dict_name)
76
+
77
+ from functools import update_wrapper
78
+ new_wrapper = update_wrapper(wrapper, function)
79
+
80
+ # type-ignore because mypy has a point here, stuffing random attributes
81
+ # into the function's dict is moderately sketchy.
82
+ new_wrapper.clear_cache = clear_cache # type: ignore[attr-defined]
83
+
84
+ return cast(F, new_wrapper)
85
+
86
+ # }}}
87
+
88
+
89
+ Error = _isl.Error
90
+
91
+ # {{{ name imports
92
+
93
+ isl_version = _isl.isl_version
94
+
95
+ Context = _isl.Context
96
+ IdList = _isl.IdList
97
+ ValList = _isl.ValList
98
+ BasicSetList = _isl.BasicSetList
99
+ BasicMapList = _isl.BasicMapList
100
+ SetList = _isl.SetList
101
+ MapList = _isl.MapList
102
+ UnionSetList = _isl.UnionSetList
103
+ ConstraintList = _isl.ConstraintList
104
+ AffList = _isl.AffList
105
+ PwAffList = _isl.PwAffList
106
+ PwMultiAffList = _isl.PwMultiAffList
107
+ AstExprList = _isl.AstExprList
108
+ AstNodeList = _isl.AstNodeList
109
+
110
+ QPolynomialList = _isl.QPolynomialList
111
+ PwQPolynomialList = _isl.PwQPolynomialList
112
+ PwQPolynomialFoldList = _isl.PwQPolynomialFoldList
113
+
114
+ UnionPwAffList = _isl.UnionPwAffList
115
+ UnionPwMultiAffList = _isl.UnionPwMultiAffList
116
+ UnionMapList = _isl.UnionMapList
117
+ UnionSetList = _isl.UnionSetList
118
+
119
+ IdToAstExpr = _isl.IdToAstExpr
120
+ Printer = _isl.Printer
121
+ Val = _isl.Val
122
+ MultiVal = _isl.MultiVal
123
+ Vec = _isl.Vec
124
+ Mat = _isl.Mat
125
+ FixedBox = _isl.FixedBox
126
+ Aff = _isl.Aff
127
+ PwAff = _isl.PwAff
128
+ UnionPwAff = _isl.UnionPwAff
129
+ MultiAff = _isl.MultiAff
130
+ MultiPwAff = _isl.MultiPwAff
131
+ PwMultiAff = _isl.PwMultiAff
132
+ UnionPwMultiAff = _isl.UnionPwMultiAff
133
+ UnionPwAffList = _isl.UnionPwAffList
134
+ MultiUnionPwAff = _isl.MultiUnionPwAff
135
+ Id = _isl.Id
136
+ MultiId = _isl.MultiId
137
+ Constraint = _isl.Constraint
138
+ Space = _isl.Space
139
+ LocalSpace = _isl.LocalSpace
140
+ BasicSet = _isl.BasicSet
141
+ BasicMap = _isl.BasicMap
142
+ Set = _isl.Set
143
+ Map = _isl.Map
144
+ UnionMap = _isl.UnionMap
145
+ UnionSet = _isl.UnionSet
146
+ Point = _isl.Point
147
+ Vertex = _isl.Vertex
148
+ Cell = _isl.Cell
149
+ Vertices = _isl.Vertices
150
+ StrideInfo = _isl.StrideInfo
151
+ QPolynomialFold = _isl.QPolynomialFold
152
+ PwQPolynomialFold = _isl.PwQPolynomialFold
153
+ UnionPwQPolynomialFold = _isl.UnionPwQPolynomialFold
154
+ UnionPwQPolynomial = _isl.UnionPwQPolynomial
155
+ QPolynomial = _isl.QPolynomial
156
+ PwQPolynomial = _isl.PwQPolynomial
157
+ Term = _isl.Term
158
+ ScheduleConstraints = _isl.ScheduleConstraints
159
+ ScheduleNode = _isl.ScheduleNode
160
+ Schedule = _isl.Schedule
161
+ AccessInfo = _isl.AccessInfo
162
+ Flow = _isl.Flow
163
+ Restriction = _isl.Restriction
164
+ UnionAccessInfo = _isl.UnionAccessInfo
165
+ UnionFlow = _isl.UnionFlow
166
+ AstExpr = _isl.AstExpr
167
+ AstNode = _isl.AstNode
168
+ AstPrintOptions = _isl.AstPrintOptions
169
+ AstBuild = _isl.AstBuild
170
+
171
+ error = _isl.error
172
+ stat = _isl.stat
173
+ dim_type = _isl.dim_type
174
+ schedule_node_type = _isl.schedule_node_type
175
+ ast_expr_op_type = _isl.ast_expr_op_type
176
+ ast_expr_type = _isl.ast_expr_type
177
+ ast_node_type = _isl.ast_node_type
178
+ ast_loop_type = _isl.ast_loop_type
179
+ fold = _isl.fold
180
+ format = _isl.format
181
+ yaml_style = _isl.yaml_style
182
+ bound = _isl.bound
183
+ on_error = _isl.on_error
184
+ schedule_algorithm = _isl.schedule_algorithm
185
+
186
+ # backward compatibility
187
+ ast_op_type = _isl.ast_expr_op_type
188
+
189
+ # }}}
190
+
191
+
192
+ _CHECK_DIM_TYPES = [
193
+ dim_type.in_, dim_type.param, dim_type.set]
194
+
195
+ ALL_CLASSES = tuple(getattr(_isl, cls) for cls in dir(_isl) if cls[0].isupper())
196
+ EXPR_CLASSES = tuple(cls for cls in ALL_CLASSES
197
+ if "Aff" in cls.__name__ or "Polynomial" in cls.__name__)
198
+
199
+ DEFAULT_CONTEXT = Context()
200
+
201
+
202
+ def _get_default_context():
203
+ """A callable to get the default context for the benefit of Python's
204
+ ``__reduce__`` protocol.
205
+ """
206
+ return DEFAULT_CONTEXT
207
+
208
+
209
+ def _read_from_str_wrapper(cls, context, s, dims_with_apostrophes):
210
+ """A callable to reconstitute instances from strings for the benefit
211
+ of Python's ``__reduce__`` protocol.
212
+ """
213
+ cls_from_str = cls.read_from_str(context, s)
214
+
215
+ # Apostrophes in dim names have been lost, put them back
216
+ for dim_name, (dim_type, dim_idx) in dims_with_apostrophes.items():
217
+ cls_from_str = cls_from_str.set_dim_name(dim_type, dim_idx, dim_name)
218
+
219
+ return cls_from_str
220
+
221
+
222
+ def _add_functionality():
223
+ import islpy._isl as _isl # noqa
224
+
225
+ # {{{ dim_type
226
+
227
+ def dim_type_reduce(v):
228
+ return (dim_type, (int(v),))
229
+
230
+ dim_type.__reduce__ = dim_type_reduce
231
+
232
+ # }}}
233
+
234
+ # {{{ Context
235
+
236
+ def context_reduce(self):
237
+ if self._wraps_same_instance_as(DEFAULT_CONTEXT):
238
+ return (_get_default_context, ())
239
+ else:
240
+ return (Context, ())
241
+
242
+ def context_eq(self, other):
243
+ return isinstance(other, Context) and self._wraps_same_instance_as(other)
244
+
245
+ def context_ne(self, other):
246
+ return not self.__eq__(other)
247
+
248
+ Context.__reduce__ = context_reduce
249
+ Context.__eq__ = context_eq
250
+ Context.__ne__ = context_ne
251
+
252
+ # }}}
253
+
254
+ # {{{ generic initialization, pickling
255
+
256
+ def generic_reduce(self):
257
+ ctx = self.get_ctx()
258
+ prn = Printer.to_str(ctx)
259
+ prn = getattr(prn, f"print_{self._base_name}")(self)
260
+
261
+ # Reconstructing from string will remove apostrophes in dim names,
262
+ # so keep track of dim names with apostrophes
263
+ dims_with_apostrophes = {
264
+ dname: pos for dname, pos in self.get_var_dict().items()
265
+ if "'" in dname}
266
+
267
+ return (
268
+ _read_from_str_wrapper,
269
+ (type(self), ctx, prn.get_str(), dims_with_apostrophes))
270
+
271
+ for cls in ALL_CLASSES:
272
+ if hasattr(cls, "read_from_str"):
273
+ cls.__reduce__ = generic_reduce
274
+
275
+ # }}}
276
+
277
+ # {{{ printing
278
+
279
+ def generic_str(self):
280
+ prn = Printer.to_str(self.get_ctx())
281
+ getattr(prn, f"print_{self._base_name}")(self)
282
+ return prn.get_str()
283
+
284
+ def generic_repr(self):
285
+ prn = Printer.to_str(self.get_ctx())
286
+ getattr(prn, f"print_{self._base_name}")(self)
287
+ return f'{type(self).__name__}("{prn.get_str()}")'
288
+
289
+ for cls in ALL_CLASSES:
290
+ if (hasattr(cls, "_base_name")
291
+ and hasattr(Printer, f"print_{cls._base_name}")):
292
+ cls.__str__ = generic_str
293
+ cls.__repr__ = generic_repr
294
+
295
+ if not hasattr(cls, "__hash__"):
296
+ raise AssertionError(f"not hashable: {cls}")
297
+
298
+ # }}}
299
+
300
+ # {{{ Python set-like behavior
301
+
302
+ def obj_or(self, other):
303
+ try:
304
+ return self.union(other)
305
+ except TypeError:
306
+ return NotImplemented
307
+
308
+ def obj_and(self, other):
309
+ try:
310
+ return self.intersect(other)
311
+ except TypeError:
312
+ return NotImplemented
313
+
314
+ def obj_sub(self, other):
315
+ try:
316
+ return self.subtract(other)
317
+ except TypeError:
318
+ return NotImplemented
319
+
320
+ for cls in [BasicSet, BasicMap, Set, Map]:
321
+ cls.__or__ = obj_or
322
+ cls.__ror__ = obj_or
323
+ cls.__and__ = obj_and
324
+ cls.__rand__ = obj_and
325
+ cls.__sub__ = obj_sub
326
+
327
+ # }}}
328
+
329
+ # {{{ Space
330
+
331
+ def space_get_id_dict(self, dimtype=None):
332
+ """Return a dictionary mapping variable :class:`Id` instances to tuples
333
+ of (:class:`dim_type`, index).
334
+
335
+ :param dimtype: None to get all variables, otherwise
336
+ one of :class:`dim_type`.
337
+ """
338
+ result = {}
339
+
340
+ def set_dim_id(name, tp, idx):
341
+ if name in result:
342
+ raise RuntimeError(f"non-unique var id '{name}' encountered")
343
+ result[name] = tp, idx
344
+
345
+ if dimtype is None:
346
+ types = _CHECK_DIM_TYPES
347
+ else:
348
+ types = [dimtype]
349
+
350
+ for tp in types:
351
+ for i in range(self.dim(tp)):
352
+ name = self.get_dim_id(tp, i)
353
+ if name is not None:
354
+ set_dim_id(name, tp, i)
355
+
356
+ return result
357
+
358
+ def space_get_var_dict(self, dimtype=None, ignore_out=False):
359
+ """Return a dictionary mapping variable names to tuples of
360
+ (:class:`dim_type`, index).
361
+
362
+ :param dimtype: None to get all variables, otherwise
363
+ one of :class:`dim_type`.
364
+ """
365
+ result = {}
366
+
367
+ def set_dim_name(name, tp, idx):
368
+ if name in result:
369
+ raise RuntimeError(f"non-unique var name '{name}' encountered")
370
+ result[name] = tp, idx
371
+
372
+ if dimtype is None:
373
+ types = _CHECK_DIM_TYPES
374
+ if ignore_out:
375
+ types = types[:]
376
+ types.remove(dim_type.out)
377
+ else:
378
+ types = [dimtype]
379
+
380
+ for tp in types:
381
+ for i in range(self.dim(tp)):
382
+ name = self.get_dim_name(tp, i)
383
+ if name is not None:
384
+ set_dim_name(name, tp, i)
385
+
386
+ return result
387
+
388
+ def space_create_from_names(ctx, set=None, in_=None, out=None, params=()):
389
+ """Create a :class:`Space` from lists of variable names.
390
+
391
+ :param set_: names of `set`-type variables.
392
+ :param in_: names of `in`-type variables.
393
+ :param out: names of `out`-type variables.
394
+ :param params: names of parameter-type variables.
395
+ """
396
+ dt = dim_type
397
+
398
+ if set is not None:
399
+ if in_ is not None or out is not None:
400
+ raise RuntimeError("must pass only one of set / (in_,out)")
401
+
402
+ result = Space.set_alloc(ctx, nparam=len(params),
403
+ dim=len(set))
404
+
405
+ for i, name in enumerate(set):
406
+ result = result.set_dim_name(dt.set, i, name)
407
+
408
+ elif in_ is not None and out is not None:
409
+ if set is not None:
410
+ raise RuntimeError("must pass only one of set / (in_,out)")
411
+
412
+ result = Space.alloc(ctx, nparam=len(params),
413
+ n_in=len(in_), n_out=len(out))
414
+
415
+ for i, name in enumerate(in_):
416
+ result = result.set_dim_name(dt.in_, i, name)
417
+
418
+ for i, name in enumerate(out):
419
+ result = result.set_dim_name(dt.out, i, name)
420
+ else:
421
+ raise RuntimeError("invalid parameter combination")
422
+
423
+ for i, name in enumerate(params):
424
+ result = result.set_dim_name(dt.param, i, name)
425
+
426
+ return result
427
+
428
+ Space.create_from_names = staticmethod(space_create_from_names)
429
+ Space.get_var_dict = space_get_var_dict
430
+ Space.get_id_dict = space_get_id_dict
431
+
432
+ # }}}
433
+
434
+ # {{{ coefficient wrangling
435
+
436
+ def obj_set_coefficients(self, dim_tp, args):
437
+ """
438
+ :param dim_tp: :class:`dim_type`
439
+ :param args: :class:`list` of coefficients, for indices `0..len(args)-1`.
440
+
441
+ .. versionchanged:: 2011.3
442
+ New for :class:`Aff`
443
+ """
444
+ for i, coeff in enumerate(args):
445
+ self = self.set_coefficient_val(dim_tp, i, coeff)
446
+
447
+ return self
448
+
449
+ def obj_set_coefficients_by_name(self, iterable, name_to_dim=None):
450
+ """Set the coefficients and the constant.
451
+
452
+ :param iterable: a :class:`dict` or iterable of :class:`tuple`
453
+ instances mapping variable names to their coefficients.
454
+ The constant is set to the value of the key '1'.
455
+
456
+ .. versionchanged:: 2011.3
457
+ New for :class:`Aff`
458
+ """
459
+ try:
460
+ iterable = list(iterable.items())
461
+ except AttributeError:
462
+ pass
463
+
464
+ if name_to_dim is None:
465
+ name_to_dim = self.get_space().get_var_dict()
466
+
467
+ for name, coeff in iterable:
468
+ if name == 1:
469
+ self = self.set_constant_val(coeff)
470
+ else:
471
+ tp, idx = name_to_dim[name]
472
+ self = self.set_coefficient_val(tp, idx, coeff)
473
+
474
+ return self
475
+
476
+ def obj_get_coefficients_by_name(self, dimtype=None, dim_to_name=None):
477
+ """Return a dictionary mapping variable names to coefficients.
478
+
479
+ :param dimtype: None to get all variables, otherwise
480
+ one of :class:`dim_type`.
481
+
482
+ .. versionchanged:: 2011.3
483
+ New for :class:`Aff`
484
+ """
485
+ if dimtype is None:
486
+ types = _CHECK_DIM_TYPES
487
+ else:
488
+ types = [dimtype]
489
+
490
+ result = {}
491
+ for tp in types:
492
+ for i in range(self.get_space().dim(tp)):
493
+ coeff = self.get_coefficient_val(tp, i)
494
+ if coeff:
495
+ if dim_to_name is None:
496
+ name = self.get_dim_name(tp, i)
497
+ else:
498
+ name = dim_to_name[tp, i]
499
+
500
+ result[name] = coeff
501
+
502
+ const = self.get_constant_val()
503
+ if const:
504
+ result[1] = const
505
+
506
+ return result
507
+
508
+ for coeff_class in [Constraint, Aff]:
509
+ coeff_class.set_coefficients = obj_set_coefficients
510
+ coeff_class.set_coefficients_by_name = obj_set_coefficients_by_name
511
+ coeff_class.get_coefficients_by_name = obj_get_coefficients_by_name
512
+
513
+ # }}}
514
+
515
+ # {{{ Id
516
+
517
+ Id.user = property(Id.get_user)
518
+ Id.name = property(Id.get_name)
519
+
520
+ # }}}
521
+
522
+ # {{{ Constraint
523
+
524
+ def eq_from_names(space, coefficients=None):
525
+ """Create a constraint `const + coeff_1*var_1 +... == 0`.
526
+
527
+ :param space: :class:`Space`
528
+ :param coefficients: a :class:`dict` or iterable of :class:`tuple`
529
+ instances mapping variable names to their coefficients
530
+ The constant is set to the value of the key '1'.
531
+
532
+ .. versionchanged:: 2011.3
533
+ Eliminated the separate *const* parameter.
534
+ """
535
+ if coefficients is None:
536
+ coefficients = {}
537
+ c = Constraint.equality_alloc(space)
538
+ return c.set_coefficients_by_name(coefficients)
539
+
540
+ def ineq_from_names(space, coefficients=None):
541
+ """Create a constraint `const + coeff_1*var_1 +... >= 0`.
542
+
543
+ :param space: :class:`Space`
544
+ :param coefficients: a :class:`dict` or iterable of :class:`tuple`
545
+ instances mapping variable names to their coefficients
546
+ The constant is set to the value of the key '1'.
547
+
548
+ .. versionchanged:: 2011.3
549
+ Eliminated the separate *const* parameter.
550
+ """
551
+ if coefficients is None:
552
+ coefficients = {}
553
+ c = Constraint.inequality_alloc(space)
554
+ return c.set_coefficients_by_name(coefficients)
555
+
556
+ Constraint.eq_from_names = staticmethod(eq_from_names)
557
+ Constraint.ineq_from_names = staticmethod(ineq_from_names)
558
+
559
+ # }}}
560
+
561
+ def basic_obj_get_constraints(self):
562
+ """Get a list of constraints."""
563
+ result = []
564
+ self.foreach_constraint(result.append)
565
+ return result
566
+
567
+ # {{{ BasicSet
568
+
569
+ BasicSet.get_constraints = basic_obj_get_constraints
570
+
571
+ # }}}
572
+
573
+ # {{{ BasicMap
574
+
575
+ BasicMap.get_constraints = basic_obj_get_constraints
576
+
577
+ # }}}
578
+
579
+ # {{{ Set
580
+
581
+ def set_get_basic_sets(self):
582
+ """Get the list of :class:`BasicSet` instances in this :class:`Set`."""
583
+ result = []
584
+ self.foreach_basic_set(result.append)
585
+ return result
586
+
587
+ Set.get_basic_sets = set_get_basic_sets
588
+ BasicSet.get_basic_sets = set_get_basic_sets
589
+
590
+ # }}}
591
+
592
+ # {{{ Map
593
+
594
+ def map_get_basic_maps(self):
595
+ """Get the list of :class:`BasicMap` instances in this :class:`Map`."""
596
+ result = []
597
+ self.foreach_basic_map(result.append)
598
+ return result
599
+
600
+ Map.get_basic_maps = map_get_basic_maps
601
+
602
+ # }}}
603
+
604
+ # {{{ common functionality
605
+
606
+ def obj_get_id_dict(self, dimtype=None):
607
+ """Return a dictionary mapping :class:`Id` instances to tuples of
608
+ (:class:`dim_type`, index).
609
+
610
+ :param dimtype: None to get all variables, otherwise
611
+ one of :class:`dim_type`.
612
+ """
613
+ return self.get_space().get_id_dict(dimtype)
614
+
615
+ @_memoize_on_first_arg
616
+ def obj_get_var_dict(self, dimtype=None):
617
+ """Return a dictionary mapping variable names to tuples of
618
+ (:class:`dim_type`, index).
619
+
620
+ :param dimtype: None to get all variables, otherwise
621
+ one of :class:`dim_type`.
622
+ """
623
+ return self.get_space().get_var_dict(
624
+ dimtype, ignore_out=isinstance(self, EXPR_CLASSES))
625
+
626
+ def obj_get_var_ids(self, dimtype):
627
+ """Return a list of :class:`Id` instances for :class:`dim_type` *dimtype*."""
628
+ return [self.get_dim_name(dimtype, i) for i in range(self.dim(dimtype))]
629
+
630
+ @_memoize_on_first_arg
631
+ def obj_get_var_names(self, dimtype):
632
+ """Return a list of dim names (in order) for :class:`dim_type` *dimtype*."""
633
+ return [self.get_dim_name(dimtype, i) for i in range(self.dim(dimtype))]
634
+
635
+ for cls in ALL_CLASSES:
636
+ if hasattr(cls, "get_space") and cls is not Space:
637
+ cls.get_id_dict = obj_get_id_dict
638
+ cls.get_var_dict = obj_get_var_dict
639
+ cls.get_var_ids = obj_get_var_ids
640
+ cls.get_var_names = obj_get_var_names
641
+ cls.space = property(cls.get_space)
642
+
643
+ # }}}
644
+
645
+ # {{{ piecewise
646
+
647
+ def pwaff_get_pieces(self):
648
+ """
649
+ :return: list of (:class:`Set`, :class:`Aff`)
650
+ """
651
+
652
+ result = []
653
+
654
+ def append_tuple(*args):
655
+ result.append(args)
656
+
657
+ self.foreach_piece(append_tuple)
658
+ return result
659
+
660
+ def pwqpolynomial_get_pieces(self):
661
+ """
662
+ :return: list of (:class:`Set`, :class:`QPolynomial`)
663
+ """
664
+
665
+ result = []
666
+
667
+ def append_tuple(*args):
668
+ result.append(args)
669
+
670
+ self.foreach_piece(append_tuple)
671
+ return result
672
+
673
+ def pw_get_aggregate_domain(self):
674
+ """
675
+ :return: a :class:`Set` that is the union of the domains of all pieces
676
+ """
677
+
678
+ result = Set.empty(self.get_domain_space())
679
+ for dom, _ in self.get_pieces():
680
+ result = result.union(dom)
681
+
682
+ return result
683
+
684
+ PwAff.get_pieces = pwaff_get_pieces
685
+ Aff.get_pieces = pwaff_get_pieces
686
+ PwAff.get_aggregate_domain = pw_get_aggregate_domain
687
+
688
+ PwQPolynomial.get_pieces = pwqpolynomial_get_pieces
689
+ PwQPolynomial.get_aggregate_domain = pw_get_aggregate_domain
690
+
691
+ # }}}
692
+
693
+ # {{{ QPolynomial
694
+
695
+ def qpolynomial_get_terms(self):
696
+ """Get the list of :class:`Term` instances in this :class:`QPolynomial`."""
697
+ result = []
698
+ self.foreach_term(result.append)
699
+ return result
700
+
701
+ QPolynomial.get_terms = qpolynomial_get_terms
702
+
703
+ # }}}
704
+
705
+ # {{{ PwQPolynomial
706
+
707
+ def pwqpolynomial_eval_with_dict(self, value_dict):
708
+ """Evaluates *self* for the parameters specified by
709
+ *value_dict*, which maps parameter names to their values.
710
+ """
711
+
712
+ pt = Point.zero(self.space.params())
713
+
714
+ for i in range(self.space.dim(dim_type.param)):
715
+ par_name = self.space.get_dim_name(dim_type.param, i)
716
+ pt = pt.set_coordinate_val(
717
+ dim_type.param, i, value_dict[par_name])
718
+
719
+ return self.eval(pt).to_python()
720
+
721
+ PwQPolynomial.eval_with_dict = pwqpolynomial_eval_with_dict
722
+
723
+ # }}}
724
+
725
+ # {{{ arithmetic
726
+
727
+ def _number_to_expr_like(template, num):
728
+ number_aff = Aff.zero_on_domain(template.get_domain_space())
729
+ number_aff = number_aff.set_constant_val(num)
730
+
731
+ if isinstance(template, Aff):
732
+ return number_aff
733
+ if isinstance(template, QPolynomial):
734
+ return QPolynomial.from_aff(number_aff)
735
+
736
+ # everything else is piecewise
737
+
738
+ if template.get_pieces():
739
+ number_pw_aff = PwAff.empty(template.get_space())
740
+ for set, _ in template.get_pieces():
741
+ number_pw_aff = set.indicator_function().cond(
742
+ number_aff, number_pw_aff)
743
+ else:
744
+ number_pw_aff = PwAff.alloc(
745
+ Set.universe(template.domain().space),
746
+ number_aff)
747
+
748
+ if isinstance(template, PwAff):
749
+ return number_pw_aff
750
+
751
+ elif isinstance(template, PwQPolynomial):
752
+ return PwQPolynomial.from_pw_aff(number_pw_aff)
753
+
754
+ else:
755
+ raise TypeError("unexpected template type")
756
+
757
+ ARITH_CLASSES = (Aff, PwAff, QPolynomial, PwQPolynomial) # noqa
758
+
759
+ def expr_like_add(self, other):
760
+ if not isinstance(other, ARITH_CLASSES):
761
+ other = _number_to_expr_like(self, other)
762
+
763
+ try:
764
+ return self.add(other)
765
+ except TypeError:
766
+ return NotImplemented
767
+
768
+ def expr_like_sub(self, other):
769
+ if not isinstance(other, ARITH_CLASSES):
770
+ other = _number_to_expr_like(self, other)
771
+
772
+ try:
773
+ return self.sub(other)
774
+ except TypeError:
775
+ return NotImplemented
776
+
777
+ def expr_like_rsub(self, other):
778
+ if not isinstance(other, ARITH_CLASSES):
779
+ other = _number_to_expr_like(self, other)
780
+
781
+ return -self + other
782
+
783
+ def expr_like_mul(self, other):
784
+ if not isinstance(other, ARITH_CLASSES):
785
+ other = _number_to_expr_like(self, other)
786
+
787
+ try:
788
+ return self.mul(other)
789
+ except TypeError:
790
+ return NotImplemented
791
+
792
+ def expr_like_floordiv(self, other):
793
+ return self.scale_down_val(other).floor()
794
+
795
+ for expr_like_class in ARITH_CLASSES:
796
+ expr_like_class.__add__ = expr_like_add
797
+ expr_like_class.__radd__ = expr_like_add
798
+ expr_like_class.__sub__ = expr_like_sub
799
+ expr_like_class.__rsub__ = expr_like_rsub
800
+ expr_like_class.__mul__ = expr_like_mul
801
+ expr_like_class.__rmul__ = expr_like_mul
802
+ expr_like_class.__neg__ = expr_like_class.neg
803
+
804
+ for qpoly_class in [QPolynomial, PwQPolynomial]:
805
+ qpoly_class.__pow__ = qpoly_class.pow
806
+
807
+ for aff_class in [Aff, PwAff]:
808
+ aff_class.__mod__ = aff_class.mod_val
809
+ aff_class.__floordiv__ = expr_like_floordiv
810
+
811
+ # }}}
812
+
813
+ # {{{ Val
814
+
815
+ def val_rsub(self, other):
816
+ return -self + other
817
+
818
+ def val_bool(self):
819
+ return not self.is_zero()
820
+
821
+ def val_repr(self):
822
+ return f'{type(self).__name__}("{self.to_str()}")'
823
+
824
+ def val_to_python(self):
825
+ if not self.is_int():
826
+ raise ValueError("can only convert integer Val to python")
827
+
828
+ return int(self.to_str())
829
+
830
+ Val.__add__ = Val.add
831
+ Val.__radd__ = Val.add
832
+ Val.__sub__ = Val.sub
833
+ Val.__rsub__ = val_rsub
834
+ Val.__mul__ = Val.mul
835
+ Val.__rmul__ = Val.mul
836
+ Val.__neg__ = Val.neg
837
+ Val.__mod__ = Val.mod
838
+ Val.__bool__ = Val.__nonzero__ = val_bool
839
+
840
+ Val.__lt__ = Val.lt
841
+ Val.__gt__ = Val.gt
842
+ Val.__le__ = Val.le
843
+ Val.__ge__ = Val.ge
844
+ Val.__eq__ = Val.eq
845
+ Val.__ne__ = Val.ne
846
+
847
+ Val.__repr__ = val_repr
848
+ Val.__str__ = Val.to_str
849
+ Val.to_python = val_to_python
850
+
851
+ # }}}
852
+
853
+ # {{{ rich comparisons
854
+
855
+ def obj_eq(self, other):
856
+ assert self.get_ctx() == other.get_ctx(), (
857
+ "Equality-comparing two objects from different ISL Contexts "
858
+ "will likely lead to entertaining (but never useful) results. "
859
+ "In particular, Spaces with matching names will no longer be "
860
+ "equal.")
861
+
862
+ return self.is_equal(other)
863
+
864
+ def obj_ne(self, other):
865
+ return not self.__eq__(other)
866
+
867
+ for cls in ALL_CLASSES:
868
+ if hasattr(cls, "is_equal"):
869
+ cls.__eq__ = obj_eq
870
+ cls.__ne__ = obj_ne
871
+
872
+ def obj_lt(self, other):
873
+ return self.is_strict_subset(other)
874
+
875
+ def obj_le(self, other):
876
+ return self.is_subset(other)
877
+
878
+ def obj_gt(self, other):
879
+ return other.is_strict_subset(self)
880
+
881
+ def obj_ge(self, other):
882
+ return other.is_subset(self)
883
+
884
+ for cls in [BasicSet, BasicMap, Set, Map]:
885
+ cls.__lt__ = obj_lt
886
+ cls.__le__ = obj_le
887
+ cls.__gt__ = obj_gt
888
+ cls.__ge__ = obj_ge
889
+
890
+ # }}}
891
+
892
+ # {{{ project_out_except
893
+
894
+ def obj_project_out_except(obj, names, types):
895
+ """
896
+ :param types: list of :class:`dim_type` determining
897
+ the types of axes to project out
898
+ :param names: names of axes matching the above which
899
+ should be left alone by the projection
900
+
901
+ .. versionadded:: 2011.3
902
+ """
903
+
904
+ for tp in types:
905
+ while True:
906
+ space = obj.get_space()
907
+ var_dict = space.get_var_dict(tp)
908
+
909
+ all_indices = set(range(space.dim(tp)))
910
+ leftover_indices = {var_dict[name][1] for name in names
911
+ if name in var_dict}
912
+ project_indices = all_indices-leftover_indices
913
+ if not project_indices:
914
+ break
915
+
916
+ min_index = min(project_indices)
917
+ count = 1
918
+ while min_index+count in project_indices:
919
+ count += 1
920
+
921
+ obj = obj.project_out(tp, min_index, count)
922
+
923
+ return obj
924
+
925
+ # }}}
926
+
927
+ # {{{ eliminate_except
928
+
929
+ def obj_eliminate_except(obj, names, types):
930
+ """
931
+ :param types: list of :class:`dim_type` determining
932
+ the types of axes to eliminate
933
+ :param names: names of axes matching the above which
934
+ should be left alone by the eliminate
935
+
936
+ .. versionadded:: 2011.3
937
+ """
938
+
939
+ for tp in types:
940
+ space = obj.get_space()
941
+ var_dict = space.get_var_dict(tp)
942
+ to_eliminate = (
943
+ set(range(space.dim(tp)))
944
+ - {var_dict[name][1] for name in names
945
+ if name in var_dict})
946
+
947
+ while to_eliminate:
948
+ min_index = min(to_eliminate)
949
+ count = 1
950
+ while min_index+count in to_eliminate:
951
+ count += 1
952
+
953
+ obj = obj.eliminate(tp, min_index, count)
954
+
955
+ to_eliminate -= set(range(min_index, min_index+count))
956
+
957
+ return obj
958
+
959
+ # }}}
960
+
961
+ # {{{ add_constraints
962
+
963
+ def obj_add_constraints(obj, constraints):
964
+ """
965
+ .. versionadded:: 2011.3
966
+ """
967
+
968
+ for cns in constraints:
969
+ obj = obj.add_constraint(cns)
970
+
971
+ return obj
972
+
973
+ # }}}
974
+
975
+ for c in [BasicSet, BasicMap, Set, Map]:
976
+ c.project_out_except = obj_project_out_except
977
+ c.add_constraints = obj_add_constraints
978
+
979
+ for c in [BasicSet, Set]:
980
+ c.eliminate_except = obj_eliminate_except
981
+
982
+
983
+ _add_functionality()
984
+
985
+
986
+ def _back_to_basic(new_obj, old_obj):
987
+ # Work around set_dim_id not being available for Basic{Set,Map}
988
+ if isinstance(old_obj, BasicSet) and isinstance(new_obj, Set):
989
+ bsets = new_obj.get_basic_sets()
990
+
991
+ if len(bsets) == 0:
992
+ bset = BasicSet.universe(new_obj.space).complement()
993
+ else:
994
+ bset, = bsets
995
+
996
+ return bset
997
+
998
+ if isinstance(old_obj, BasicMap) and isinstance(new_obj, Map):
999
+ bmaps = new_obj.get_basic_maps()
1000
+
1001
+ if len(bmaps) == 0:
1002
+ bmap = BasicMap.universe(new_obj.space).complement()
1003
+ else:
1004
+ bmap, = bmaps
1005
+
1006
+ return bmap
1007
+
1008
+ return new_obj
1009
+
1010
+
1011
+ def _set_dim_id(obj, dt, idx, id):
1012
+ return _back_to_basic(obj.set_dim_id(dt, idx, id), obj)
1013
+
1014
+
1015
+ def _align_dim_type(template_dt, obj, template, obj_bigger_ok, obj_names,
1016
+ template_names):
1017
+
1018
+ # {{{ deal with Aff, PwAff
1019
+
1020
+ # The technique below will not work for PwAff et al, because there is *only*
1021
+ # the 'param' dim_type, and we are not allowed to move dims around in there.
1022
+ # We'll make isl do the work, using align_params.
1023
+
1024
+ if template_dt == dim_type.param and isinstance(obj, (Aff, PwAff)):
1025
+ if not isinstance(template, Space):
1026
+ template_space = template.space
1027
+ else:
1028
+ template_space = template
1029
+
1030
+ if not obj_bigger_ok:
1031
+ if (obj.dim(template_dt) > template.dim(template_dt)
1032
+ or not set(obj.get_var_dict()) <= set(template.get_var_dict())):
1033
+ raise Error("obj has leftover dimensions after alignment")
1034
+ return obj.align_params(template_space)
1035
+
1036
+ # }}}
1037
+
1038
+ if None in template_names:
1039
+ all_nones = [None] * len(template_names)
1040
+ if template_names == all_nones and obj_names == all_nones:
1041
+ # that's ok
1042
+ return obj
1043
+
1044
+ raise Error("template may not contain any unnamed dimensions")
1045
+
1046
+ obj_names = set(obj_names) - {None}
1047
+ template_names = set(template_names) - {None}
1048
+
1049
+ names_in_both = obj_names & template_names
1050
+
1051
+ tgt_idx = 0
1052
+ while tgt_idx < template.dim(template_dt):
1053
+ tgt_id = template.get_dim_id(template_dt, tgt_idx)
1054
+ tgt_name = tgt_id.name
1055
+
1056
+ if tgt_name in names_in_both:
1057
+ if (obj.dim(template_dt) > tgt_idx
1058
+ and tgt_name == obj.get_dim_name(template_dt, tgt_idx)):
1059
+ pass
1060
+
1061
+ else:
1062
+ src_dt, src_idx = obj.get_var_dict()[tgt_name]
1063
+
1064
+ if src_dt == template_dt:
1065
+ assert src_idx > tgt_idx
1066
+
1067
+ # isl requires move_dims to be between different types.
1068
+ # Not sure why. Let's make it happy.
1069
+ other_dt = dim_type.param
1070
+ if src_dt == other_dt:
1071
+ other_dt = dim_type.out
1072
+
1073
+ other_dt_dim = obj.dim(other_dt)
1074
+ obj = obj.move_dims(other_dt, other_dt_dim, src_dt, src_idx, 1)
1075
+ obj = obj.move_dims(
1076
+ template_dt, tgt_idx, other_dt, other_dt_dim, 1)
1077
+ else:
1078
+ obj = obj.move_dims(template_dt, tgt_idx, src_dt, src_idx, 1)
1079
+
1080
+ # names are same, make Ids the same, too
1081
+ obj = _set_dim_id(obj, template_dt, tgt_idx, tgt_id)
1082
+
1083
+ tgt_idx += 1
1084
+ else:
1085
+ obj = obj.insert_dims(template_dt, tgt_idx, 1)
1086
+ obj = _set_dim_id(obj, template_dt, tgt_idx, tgt_id)
1087
+
1088
+ tgt_idx += 1
1089
+
1090
+ if tgt_idx < obj.dim(template_dt) and not obj_bigger_ok:
1091
+ raise Error("obj has leftover dimensions after alignment")
1092
+
1093
+ return obj
1094
+
1095
+
1096
+ def align_spaces(obj, template, obj_bigger_ok=False, across_dim_types=None):
1097
+ """
1098
+ Try to make the space in which *obj* lives the same as that of *template* by
1099
+ adding/matching named dimensions.
1100
+
1101
+ :param obj_bigger_ok: If *True*, no error is raised if the resulting *obj*
1102
+ has more dimensions than *template*.
1103
+ """
1104
+
1105
+ if across_dim_types is not None:
1106
+ from warnings import warn
1107
+ warn("across_dim_types is deprecated and should no longer be used. "
1108
+ "It never had any effect anyway.",
1109
+ DeprecationWarning, stacklevel=2)
1110
+
1111
+ have_any_param_domains = (
1112
+ isinstance(obj, (Set, BasicSet))
1113
+ and isinstance(template, (Set, BasicSet))
1114
+ and (obj.is_params() or template.is_params()))
1115
+ if have_any_param_domains:
1116
+ if obj.is_params():
1117
+ obj = type(obj).from_params(obj)
1118
+ if template.is_params():
1119
+ template = type(template).from_params(template)
1120
+
1121
+ if isinstance(template, EXPR_CLASSES):
1122
+ dim_types = _CHECK_DIM_TYPES[:]
1123
+ dim_types.remove(dim_type.out)
1124
+ else:
1125
+ dim_types = _CHECK_DIM_TYPES
1126
+
1127
+ obj_names = [
1128
+ obj.get_dim_name(dt, i)
1129
+ for dt in dim_types
1130
+ for i in range(obj.dim(dt))
1131
+ ]
1132
+ template_names = [
1133
+ template.get_dim_name(dt, i)
1134
+ for dt in dim_types
1135
+ for i in range(template.dim(dt))
1136
+ ]
1137
+
1138
+ for dt in dim_types:
1139
+ obj = _align_dim_type(
1140
+ dt, obj, template, obj_bigger_ok, obj_names, template_names)
1141
+
1142
+ return obj
1143
+
1144
+
1145
+ def align_two(obj1, obj2, across_dim_types=None):
1146
+ """Align the spaces of two objects, potentially modifying both of them.
1147
+
1148
+ See also :func:`align_spaces`.
1149
+ """
1150
+
1151
+ if across_dim_types is not None:
1152
+ from warnings import warn
1153
+ warn("across_dim_types is deprecated and should no longer be used. "
1154
+ "It never had any effect anyway.",
1155
+ DeprecationWarning, stacklevel=2)
1156
+
1157
+ obj1 = align_spaces(obj1, obj2, obj_bigger_ok=True)
1158
+ obj2 = align_spaces(obj2, obj1, obj_bigger_ok=True)
1159
+ return (obj1, obj2)
1160
+
1161
+
1162
+ def make_zero_and_vars(set_vars, params=(), ctx=None):
1163
+ """
1164
+ :arg set_vars: an iterable of variable names, or a comma-separated string
1165
+ :arg params: an iterable of variable names, or a comma-separated string
1166
+
1167
+ :return: a dictionary from variable names (in *set_vars* and *params*)
1168
+ to :class:`PwAff` instances that represent each of the
1169
+ variables. They key '0' is also include and represents
1170
+ a :class:`PwAff` zero constant.
1171
+
1172
+ .. versionadded:: 2016.1.1
1173
+
1174
+ This function is intended to make it relatively easy to construct sets
1175
+ programmatically without resorting to string manipulation.
1176
+
1177
+ Usage example::
1178
+
1179
+ v = isl.make_zero_and_vars("i,j,k", "n")
1180
+
1181
+ myset = (
1182
+ v[0].le_set(v["i"] + v["j"])
1183
+ &
1184
+ (v["i"] + v["j"]).lt_set(v["n"])
1185
+ &
1186
+ (v[0].le_set(v["i"]))
1187
+ &
1188
+ (v["i"].le_set(13 + v["n"]))
1189
+ )
1190
+ """
1191
+ if ctx is None:
1192
+ ctx = DEFAULT_CONTEXT
1193
+
1194
+ if isinstance(set_vars, str):
1195
+ set_vars = [s.strip() for s in set_vars.split(",")]
1196
+ if isinstance(params, str):
1197
+ params = [s.strip() for s in params.split(",")]
1198
+
1199
+ space = Space.create_from_names(ctx, set=set_vars, params=params)
1200
+ return affs_from_space(space)
1201
+
1202
+
1203
+ def affs_from_space(space):
1204
+ """
1205
+ :return: a dictionary from variable names (in *set_vars* and *params*)
1206
+ to :class:`PwAff` instances that represent each of the
1207
+ variables *in*space*. They key '0' is also include and represents
1208
+ a :class:`PwAff` zero constant.
1209
+
1210
+ .. versionadded:: 2016.2
1211
+
1212
+ This function is intended to make it relatively easy to construct sets
1213
+ programmatically without resorting to string manipulation.
1214
+
1215
+ Usage example::
1216
+
1217
+ s = isl.Set("[n] -> {[i,j,k]: 0<=i,j,k<n}")
1218
+ v = isl.affs_from_space(s.space)
1219
+
1220
+ myset = (
1221
+ v[0].le_set(v["i"] + v["j"])
1222
+ &
1223
+ (v["i"] + v["j"]).lt_set(v["n"])
1224
+ &
1225
+ (v[0].le_set(v["i"]))
1226
+ &
1227
+ (v["i"].le_set(13 + v["n"]))
1228
+ )
1229
+ """
1230
+
1231
+ result = {}
1232
+
1233
+ zero = Aff.zero_on_domain(LocalSpace.from_space(space))
1234
+ result[0] = PwAff.from_aff(zero)
1235
+
1236
+ var_dict = zero.get_var_dict()
1237
+ for name, (dt, idx) in var_dict.items():
1238
+ result[name] = PwAff.from_aff(zero.set_coefficient_val(dt, idx, 1))
1239
+
1240
+ return result
1241
+
1242
+
1243
+ class SuppressedWarnings:
1244
+ def __init__(self, ctx):
1245
+ from warnings import warn
1246
+ warn("islpy.SuppressedWarnings is a deprecated no-op and will be removed "
1247
+ "in 2023. Simply remove the use of it to avoid this warning.",
1248
+ DeprecationWarning, stacklevel=1)
1249
+
1250
+ def __enter__(self):
1251
+ pass
1252
+
1253
+ def __exit__(self, type, value, traceback):
1254
+ pass
1255
+
1256
+
1257
+ # {{{ give sphinx something to import so we can produce docs
1258
+
1259
+ def _define_doc_link_names():
1260
+ class Div:
1261
+ pass
1262
+
1263
+ _isl.Div = Div
1264
+
1265
+
1266
+ _define_doc_link_names()
1267
+
1268
+ # }}}
1269
+
1270
+
1271
+ # vim: foldmethod=marker
islpy/version.py ADDED
@@ -0,0 +1,2 @@
1
+ VERSION = (2025, 1, 3)
2
+ VERSION_TEXT = ".".join(str(i) for i in VERSION)
@@ -0,0 +1,74 @@
1
+ Metadata-Version: 2.1
2
+ Name: islpy
3
+ Version: 2025.1.3
4
+ Summary: Wrapper around isl, an integer set library
5
+ Home-page: http://documen.tician.de/islpy
6
+ Author: Andreas Kloeckner
7
+ Author-email: inform@tiker.net
8
+ License: MIT
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Intended Audience :: Other Audience
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Natural Language :: English
15
+ Classifier: Programming Language :: C++
16
+ Classifier: Programming Language :: Python
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Topic :: Multimedia :: Graphics :: 3D Modeling
19
+ Classifier: Topic :: Scientific/Engineering
20
+ Classifier: Topic :: Scientific/Engineering :: Mathematics
21
+ Classifier: Topic :: Scientific/Engineering :: Physics
22
+ Classifier: Topic :: Scientific/Engineering :: Visualization
23
+ Classifier: Topic :: Software Development :: Libraries
24
+ Requires-Python: ~=3.8
25
+ Description-Content-Type: text/x-rst
26
+ Provides-Extra: test
27
+ Requires-Dist: pytest>=2; extra == "test"
28
+
29
+ islpy: Polyhedral Analysis from Python
30
+ ======================================
31
+
32
+ .. image:: https://gitlab.tiker.net/inducer/islpy/badges/main/pipeline.svg
33
+ :alt: Gitlab Build Status
34
+ :target: https://gitlab.tiker.net/inducer/islpy/commits/main
35
+ .. image:: https://github.com/inducer/islpy/workflows/CI/badge.svg?branch=main
36
+ :alt: Github Build Status
37
+ :target: https://github.com/inducer/islpy/actions?query=branch%3Amain+workflow%3ACI
38
+ .. image:: https://badge.fury.io/py/islpy.svg
39
+ :alt: Python Package Index Release Page
40
+ :target: https://pypi.org/project/islpy/
41
+ .. image:: https://zenodo.org/badge/2021524.svg
42
+ :alt: Zenodo DOI for latest release
43
+ :target: https://zenodo.org/badge/latestdoi/2021524
44
+
45
+ islpy is a Python wrapper around Sven Verdoolaege's `isl
46
+ <https://libisl.sourceforge.io/>`_, a library for manipulating sets and
47
+ relations of integer points bounded by linear constraints.
48
+
49
+ Supported operations on sets include
50
+
51
+ * intersection, union, set difference,
52
+ * emptiness check,
53
+ * convex hull,
54
+ * (integer) affine hull,
55
+ * integer projection,
56
+ * computing the lexicographic minimum using parametric integer programming,
57
+ * coalescing, and
58
+ * parametric vertex enumeration.
59
+
60
+ It also includes an ILP solver based on generalized basis reduction, transitive
61
+ closures on maps (which may encode infinite graphs), dependence analysis and
62
+ bounds on piecewise step-polynomials.
63
+
64
+ Islpy comes with comprehensive `documentation <http://documen.tician.de/islpy>`_.
65
+
66
+ *Requirements:* islpy needs a C++ compiler to build. It can optionally make use
67
+ of GMP for support of large integers.
68
+
69
+ One important thing to know about islpy is that it exposes every function in isl
70
+ that is visible in the headers, not just what isl's authors consider its
71
+ documented, public API (marked by ``__isl_export``). These (technically)
72
+ undocumented functions are marked in the islpy documentation. Many of them are useful
73
+ and essential for certain operations, but isl's API stability guarantees do not
74
+ apply to them. Use them at your own risk.
@@ -0,0 +1,7 @@
1
+ islpy/__init__.py,sha256=y5AwucXSrDi-y6FMkhK7F2-riplpxaX0zHJfromRCUw,37276
2
+ islpy/_isl.cpython-310-aarch64-linux-gnu.so,sha256=ULp3YIPysjqqD8zejlUWMagfSxeRVJfn9tp91uwIzd0,9380480
3
+ islpy/version.py,sha256=4-z_aHWm_HInGVtf7Qdnxzwp56WPcDGGammLFPORnUA,72
4
+ islpy-2025.1.3.dist-info/METADATA,sha256=K0gY_7nsMP3hAjJxU0K9xH5UgIPzpksgZuwoVkGC_Wc,3114
5
+ islpy-2025.1.3.dist-info/WHEEL,sha256=CPI1DfZRF98btTMY40kUweApfRdzrq_RfFdWFSPiZtQ,108
6
+ islpy-2025.1.3.dist-info/top_level.txt,sha256=txVjJJ85Sa3IMOF6WOh_bTkOyRDWiSW2kQOf8vSPb6M,6
7
+ islpy-2025.1.3.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: skbuild 0.18.1
3
+ Root-Is-Purelib: false
4
+ Tag: cp310-cp310-musllinux_1_2_aarch64
5
+
@@ -0,0 +1 @@
1
+ islpy