certlin 1.1__py3-none-any.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.
certlin/__init__.py ADDED
@@ -0,0 +1,20 @@
1
+ r"""Certifying linear inequality systems"""
2
+
3
+ #############################################################################
4
+ # Copyright (C) 2025 #
5
+ # Marcus S. Aichmayr (aichmayr@mathematik.uni-kassel.de) #
6
+ # #
7
+ # Distributed under the terms of the GNU General Public License (GPL) #
8
+ # either version 3, or (at your option) any later version #
9
+ # #
10
+ # http://www.gnu.org/licenses/ #
11
+ #############################################################################
12
+
13
+ from __future__ import absolute_import
14
+
15
+ from .intervals import Interval, Intervals
16
+ from .linear_inequality_systems import (
17
+ LinearInequalitySystem,
18
+ InhomogeneousSystem,
19
+ HomogeneousSystem,
20
+ )
certlin/intervals.py ADDED
@@ -0,0 +1,654 @@
1
+ """Interval classes"""
2
+
3
+ #############################################################################
4
+ # Copyright (C) 2025 #
5
+ # Marcus S. Aichmayr (aichmayr@mathematik.uni-kassel.de) #
6
+ # #
7
+ # Distributed under the terms of the GNU General Public License (GPL) #
8
+ # either version 3, or (at your option) any later version #
9
+ # #
10
+ # http://www.gnu.org/licenses/ #
11
+ #############################################################################
12
+
13
+ from __future__ import annotations
14
+
15
+ from typing import Iterator
16
+ from random import getrandbits
17
+
18
+ from sage.functions.other import floor, ceil
19
+ from sage.misc.mrange import cartesian_product_iterator
20
+ from sage.rings.continued_fraction import continued_fraction
21
+ from sage.rings.infinity import minus_infinity
22
+ from sage.rings.infinity import Infinity
23
+ from sage.rings.rational_field import QQ
24
+ from sage.structure.sage_object import SageObject
25
+
26
+ from sign_vectors import SignVector, sign_vector
27
+
28
+
29
+ class Interval(SageObject):
30
+ r"""
31
+ An interval.
32
+
33
+ Also supports variables.
34
+
35
+ EXAMPLES::
36
+
37
+ sage: from certlin import *
38
+ sage: Interval(0, 1)
39
+ [0, 1]
40
+ sage: Interval(0, 1, False, True)
41
+ (0, 1]
42
+ sage: Interval(0, 1, False, False)
43
+ (0, 1)
44
+ sage: Interval(0, 1, True, False)
45
+ [0, 1)
46
+ sage: Interval(0, 0)
47
+ {0}
48
+ sage: Interval(0, 0, False, False)
49
+ {}
50
+ sage: Interval(0, 0, True, True)
51
+ {0}
52
+ sage: Interval(0, 0, True, False)
53
+ {}
54
+ sage: Interval(0, 0, False, True)
55
+ {}
56
+ sage: Interval(-oo, 4)
57
+ (-oo, 4]
58
+ sage: Interval(-oo, 4, False, False)
59
+ (-oo, 4)
60
+ sage: I = Interval(-3, +oo, False, False)
61
+ sage: I
62
+ (-3, +oo)
63
+ sage: 0 in I
64
+ True
65
+ sage: -3 in I
66
+ False
67
+ sage: Interval.random() # random
68
+ (-1, 1)
69
+
70
+ Variables are supported, too::
71
+
72
+ sage: var("a")
73
+ a
74
+ sage: Interval(a, 1)
75
+ [a, 1]
76
+ sage: I = Interval(a, 1, False, False)
77
+ sage: I
78
+ (a, 1)
79
+ sage: I.an_element()
80
+ 1/2*a + 1/2
81
+ """
82
+ def __init__(self, lower, upper, lower_closed: bool = True, upper_closed: bool = True) -> None:
83
+ if lower > upper:
84
+ raise ValueError("The lower bound must be less than or equal to the upper bound.")
85
+
86
+ if lower == upper and (not lower_closed or not upper_closed):
87
+ lower = 0
88
+ upper = 0
89
+ lower_closed = False
90
+ upper_closed = False
91
+
92
+ self.lower = lower
93
+ self.upper = upper
94
+ if lower == minus_infinity:
95
+ lower_closed = False
96
+ if upper == Infinity:
97
+ upper_closed = False
98
+ self.lower_closed = lower_closed
99
+ self.upper_closed = upper_closed
100
+
101
+ def __contains__(self, value) -> bool:
102
+ if self.lower_closed and value == self.lower:
103
+ return True
104
+ if self.upper_closed and value == self.upper:
105
+ return True
106
+ return self.lower < value < self.upper
107
+
108
+ def _repr_(self) -> str:
109
+ if self.is_empty():
110
+ return "{}"
111
+ if self.is_pointed():
112
+ return f"{{{self.lower}}}"
113
+ return f"{'[' if self.lower_closed else '('}{'-oo' if self.lower == minus_infinity else self.lower}, {'+oo' if self.upper == Infinity else self.upper}{']' if self.upper_closed else ')'}"
114
+
115
+ def _latex_(self) -> str:
116
+ r"""
117
+ Return a LaTeX representation of the interval.
118
+
119
+ EXAMPLES::
120
+ sage: from certlin import *
121
+ sage: Interval(0, 1)._latex_()
122
+ '[0, 1]'
123
+ sage: Interval(0, 1, False, True)._latex_()
124
+ '(0, 1]'
125
+ sage: Interval(0, 0, False, False)._latex_()
126
+ '\\emptyset'
127
+ sage: Interval(0, 0)._latex_()
128
+ '\\{0\\}'
129
+ sage: Interval(-oo, 1/2)._latex_()
130
+ '(-\\infty, 1/2]'
131
+ sage: Interval(0, oo, False, False)._latex_()
132
+ '(0, \\infty)'
133
+ """
134
+ if self.is_empty():
135
+ return r"\emptyset"
136
+ if self.is_pointed():
137
+ return r"\{" + f"{self.lower}" + r"\}"
138
+ return (
139
+ ("[" if self.lower_closed else "(")
140
+ + (r"-\infty" if self.lower == minus_infinity else f"{self.lower}")
141
+ + ", "
142
+ + (r"\infty" if self.upper == Infinity else f"{self.upper}")
143
+ + ("]" if self.upper_closed else ")")
144
+ )
145
+
146
+ def __eq__(self, other) -> bool:
147
+ return self.lower == other.lower and self.upper == other.upper and self.lower_closed == other.lower_closed and self.upper_closed == other.upper_closed
148
+
149
+ def is_empty(self) -> bool:
150
+ r"""Return whether the interval is empty."""
151
+ return self.lower == self.upper and (not self.lower_closed or not self.upper_closed)
152
+
153
+ def is_open(self) -> bool:
154
+ r"""Return whether the interval is open."""
155
+ return self.lower_closed == self.upper_closed == False
156
+
157
+ def is_closed(self) -> bool:
158
+ r"""Return whether the interval is closed."""
159
+ return self.lower_closed and self.upper_closed
160
+
161
+ def is_unbounded(self) -> bool:
162
+ r"""Return whether the interval is unbounded."""
163
+ return self.lower == minus_infinity or self.upper == Infinity
164
+
165
+ def is_bounded(self) -> bool:
166
+ r"""Return whether the interval is bounded."""
167
+ return not self.is_unbounded()
168
+
169
+ def is_pointed(self) -> bool:
170
+ r"""
171
+ Return whether the interval is a point.
172
+
173
+ An interval is pointed if it has the same lower and upper bound, and both are closed.
174
+
175
+ EXAMPLES::
176
+
177
+ sage: from certlin import *
178
+ sage: Interval(0, 0).is_pointed()
179
+ True
180
+ sage: Interval(0, 1).is_pointed()
181
+ False
182
+ """
183
+ return self.lower == self.upper and self.lower_closed and self.upper_closed
184
+
185
+ def __bool__(self) -> bool:
186
+ return not self.is_empty()
187
+
188
+ def infimum(self):
189
+ r"""
190
+ Return the infimum of the interval.
191
+
192
+ EXAMPLES::
193
+
194
+ sage: from certlin import *
195
+ sage: Interval(0, 1).infimum()
196
+ 0
197
+ sage: Interval(-oo, 0).infimum()
198
+ -Infinity
199
+ sage: Interval(0, 0, False, False).infimum()
200
+ +Infinity
201
+ """
202
+ if self.is_empty():
203
+ return Infinity
204
+ return self.lower
205
+
206
+ def supremum(self):
207
+ r"""
208
+ Return the supremum of the interval.
209
+
210
+ EXAMPLES::
211
+
212
+ sage: from certlin import *
213
+ sage: Interval(0, 1).supremum()
214
+ 1
215
+ sage: Interval(0, +oo).supremum()
216
+ +Infinity
217
+ sage: Interval(0, 0, False, False).supremum()
218
+ -Infinity
219
+ """
220
+ if self.is_empty():
221
+ return minus_infinity
222
+ return self.upper
223
+
224
+ def an_element(self):
225
+ r"""
226
+ Return an element of the interval.
227
+
228
+ EXAMPLES::
229
+
230
+ sage: from certlin import *
231
+ sage: Interval(0, 1).an_element()
232
+ 0
233
+ sage: Interval(0, 1, False, True).an_element()
234
+ 1
235
+ sage: Interval(0, 1, False, False).an_element()
236
+ 1/2
237
+ sage: Interval(-oo, 0).an_element()
238
+ 0
239
+ sage: Interval(-oo, +oo).an_element()
240
+ 0
241
+ sage: Interval(5, +oo, False, False).an_element()
242
+ 6
243
+ sage: Interval(0, 0, False, False).an_element()
244
+ Traceback (most recent call last):
245
+ ...
246
+ ValueError: The interval is empty.
247
+ """
248
+ if self.is_empty():
249
+ raise ValueError("The interval is empty.")
250
+ if self.lower_closed:
251
+ return self.lower
252
+ if self.upper_closed:
253
+ return self.upper
254
+ if self.is_bounded():
255
+ return (self.lower + self.upper) / 2
256
+ if self.lower == minus_infinity:
257
+ if self.upper == Infinity:
258
+ return 0
259
+ return self.upper - 1
260
+ return self.lower + 1
261
+
262
+ def simplest_element(self):
263
+ r"""
264
+ Return the simplest rational in this interval.
265
+
266
+ OUTPUT:
267
+ If possible, an integer with smallest possible absolute value will be returned.
268
+ Otherwise, a rational number with smallest possible denominator is constructed.
269
+
270
+ EXAMPLES::
271
+
272
+ sage: from certlin import *
273
+ sage: Interval(1/2, +oo, False, False).simplest_element()
274
+ 1
275
+ sage: Interval(-oo, 1/2, False, False).simplest_element()
276
+ 0
277
+ sage: Interval(-19, 0, False, False).simplest_element()
278
+ -1
279
+ sage: Interval(0, 1, False, False).simplest_element()
280
+ 1/2
281
+ sage: Interval(-2/3, 0, False, False).simplest_element()
282
+ -1/2
283
+ sage: Interval(4/3, 3/2, False, False).simplest_element()
284
+ 7/5
285
+ sage: Interval(0, 0).simplest_element()
286
+ 0
287
+ sage: Interval(5, 5).simplest_element()
288
+ 5
289
+ sage: Interval(sqrt(2), pi/2).simplest_element()
290
+ 3/2
291
+ sage: Interval(1/2, 1/2).simplest_element()
292
+ 1/2
293
+ """
294
+ if self.is_empty():
295
+ raise ValueError("The interval is empty.")
296
+
297
+ if self.is_pointed():
298
+ return self.lower
299
+ if 0 in self:
300
+ return 0
301
+ if self.upper - self.lower > 1 or floor(self.lower) + 1 in self or ceil(self.upper) - 1 in self:
302
+ if self.lower == minus_infinity:
303
+ floor_upper = floor(self.upper)
304
+ return floor_upper if floor_upper in self else floor_upper - 1
305
+ if self.upper == Infinity:
306
+ ceil_lower = ceil(self.lower)
307
+ return ceil_lower if ceil_lower in self else ceil_lower + 1
308
+ if self.lower == 0:
309
+ return 1
310
+ if self.upper == 0:
311
+ return -1
312
+ if self.upper < 0:
313
+ floor_upper = floor(self.upper)
314
+ return floor_upper if floor_upper in self else floor_upper - 1
315
+ # self.lower > 0
316
+ ceil_lower = ceil(self.lower)
317
+ return ceil_lower if ceil_lower in self else ceil_lower + 1
318
+ return self._simplest_rational()
319
+
320
+ def _simplest_rational(self):
321
+ r"""
322
+ Find the rational with smallest denominator in a given interval.
323
+
324
+ INPUT:
325
+
326
+ - ``interval`` -- an interval that has no integer in it.
327
+
328
+ EXAMPLES::
329
+
330
+ sage: from certlin import *
331
+ sage: Interval(0, 1, False, False)._simplest_rational()
332
+ 1/2
333
+ sage: Interval(1/3, 1, False, False)._simplest_rational()
334
+ 1/2
335
+ sage: Interval(4/3, 2, False, False)._simplest_rational()
336
+ 3/2
337
+ sage: Interval(1/3, 1/2, False, False)._simplest_rational()
338
+ 2/5
339
+ sage: Interval(1/2, 241/287, False, False)._simplest_rational()
340
+ 2/3
341
+ """
342
+ def sb_child(cfl: list, left: bool):
343
+ r"""
344
+ Return a child of an element in the Stern-Brocot tree.
345
+
346
+ INPUT:
347
+
348
+ - ``cfl`` -- a list corresponding to a continued fraction
349
+ - ``left`` -- a boolean
350
+ """
351
+ if left != len(cfl) & 1: # check if odd
352
+ return cfl[:-1] + [cfl[-1] + 1]
353
+ return cfl[:-1] + [cfl[-1] - 1, 2]
354
+
355
+ cfl = [floor(self.infimum()), 2] # continued fraction representation of inf(interval) + 1/2
356
+
357
+ while True:
358
+ value = continued_fraction(cfl).value()
359
+ if value in self:
360
+ return value
361
+ if value <= self.infimum():
362
+ cfl = sb_child(cfl, left=False)
363
+ else:
364
+ cfl = sb_child(cfl, left=True)
365
+
366
+ @classmethod
367
+ def random(cls, ring=QQ, allow_infinity: bool = True, allow_empty: bool = False) -> Interval:
368
+ r"""
369
+ Generate a random interval.
370
+
371
+ EXAMPLES::
372
+
373
+ sage: from certlin import *
374
+ sage: Interval.random() # random
375
+ (-1, 1)
376
+ sage: Interval.random(ring=ZZ) # random
377
+ [-5, 3]
378
+ """
379
+ if allow_infinity and getrandbits(3) == 0:
380
+ lower = minus_infinity
381
+ else:
382
+ lower = ring.random_element()
383
+ if allow_infinity and getrandbits(3) == 0:
384
+ upper = Infinity
385
+ else:
386
+ upper = ring.random_element()
387
+ if lower > upper:
388
+ lower, upper = upper, lower
389
+ lower_closed = bool(getrandbits(1))
390
+ upper_closed = bool(getrandbits(1))
391
+
392
+ interval = cls(lower, upper, lower_closed, upper_closed)
393
+ if not allow_empty and interval.is_empty():
394
+ return cls.random(ring, allow_infinity, allow_empty)
395
+ return interval
396
+
397
+ @classmethod
398
+ def open(cls, lower, upper) -> Interval:
399
+ r"""
400
+ Return an open interval.
401
+
402
+ EXAMPLES::
403
+
404
+ sage: from certlin import *
405
+ sage: Interval.open(0, 1)
406
+ (0, 1)
407
+ """
408
+ return cls(lower, upper, False, False)
409
+
410
+ @classmethod
411
+ def closed(cls, lower, upper) -> Interval:
412
+ r"""
413
+ Return a closed interval.
414
+
415
+ EXAMPLES::
416
+
417
+ sage: from certlin import *
418
+ sage: Interval.closed(0, 1)
419
+ [0, 1]
420
+ """
421
+ return cls(lower, upper, True, True)
422
+
423
+ @classmethod
424
+ def empty(cls) -> Interval:
425
+ r"""
426
+ Return the empty interval.
427
+
428
+ EXAMPLES::
429
+
430
+ sage: from certlin import *
431
+ sage: Interval.empty()
432
+ {}
433
+ """
434
+ return cls(0, 0, False, False)
435
+
436
+
437
+ class Intervals(SageObject):
438
+ r"""
439
+ A Cartesian product of intervals.
440
+
441
+ EXAMPLES::
442
+
443
+ sage: from certlin import *
444
+ sage: Intervals([Interval(0, 1), Interval(-5, 2, False, False), Interval(0, 1)])
445
+ [0, 1] x (-5, 2) x [0, 1]
446
+ sage: vector([0, 1]) in Intervals([Interval(0, 1), Interval(-5, 2)])
447
+ True
448
+ sage: Intervals.random(3) # random
449
+ [0, +oo) x (-5, 2) x (0, 1]
450
+ """
451
+ def __init__(self, intervals: list[Interval]) -> None:
452
+ self.intervals = intervals
453
+
454
+ def __contains__(self, iterable) -> bool:
455
+ return all(entry in interval for entry, interval in zip(iterable, self.intervals))
456
+
457
+ def __len__(self) -> int:
458
+ return len(self.intervals)
459
+
460
+ def __getitem__(self, i: int) -> Interval:
461
+ return self.intervals[i]
462
+
463
+ def _repr_(self) -> str:
464
+ if len(self) == 0:
465
+ return "()"
466
+ return " x ".join(str(interval) for interval in self.intervals)
467
+
468
+ def _latex_(self) -> str:
469
+ r"""
470
+ Return a LaTeX representation of the intervals.
471
+
472
+ EXAMPLES::
473
+
474
+ sage: from certlin import *
475
+ sage: Intervals([Interval(0, 1), Interval(-5, 2, False, False)])._latex_()
476
+ '[0, 1] \\times (-5, 2)'
477
+ sage: Intervals([Interval(-oo, 1), Interval(2, 2, False, False)])._latex_()
478
+ '(-\\infty, 1] \\times \\emptyset'
479
+ """
480
+ return " \\times ".join(interval._latex_() for interval in self.intervals)
481
+
482
+ def __eq__(self, other) -> bool:
483
+ return self.intervals == other.intervals
484
+
485
+ def __hash__(self) -> int:
486
+ return hash(tuple(self.intervals))
487
+
488
+ def is_empty(self) -> bool:
489
+ r"""Return whether the intervals are empty."""
490
+ if len(self) == 0:
491
+ return True
492
+ return any(interval.is_empty() for interval in self.intervals)
493
+
494
+ def is_open(self) -> bool:
495
+ r"""Return whether all intervals are open."""
496
+ return all(interval.is_open() for interval in self.intervals)
497
+
498
+ def is_closed(self) -> bool:
499
+ r"""Return whether all intervals are closed."""
500
+ return all(interval.is_closed() for interval in self.intervals)
501
+
502
+ def is_unbounded(self) -> bool:
503
+ r"""Return whether any interval is unbounded."""
504
+ return any(interval.is_unbounded() for interval in self.intervals)
505
+
506
+ def is_bounded(self) -> bool:
507
+ r"""Return whether all intervals are bounded."""
508
+ return not self.is_unbounded()
509
+
510
+ def is_pointed(self) -> bool:
511
+ r"""Return whether all intervals are pointed."""
512
+ return all(interval.is_pointed() for interval in self.intervals)
513
+
514
+ def __bool__(self) -> bool:
515
+ return not self.is_empty()
516
+
517
+ def an_element(self):
518
+ r"""Return an element from each interval."""
519
+ return [interval.an_element() for interval in self.intervals]
520
+
521
+ def simplest_element(self):
522
+ r"""Return the simplest element from each interval."""
523
+ return [interval.simplest_element() for interval in self.intervals]
524
+
525
+ def __iter__(self):
526
+ return iter(self.intervals)
527
+
528
+ @classmethod
529
+ def random(cls, length: int, ring=QQ) -> Intervals:
530
+ r"""
531
+ Generate a random list of intervals.
532
+
533
+ EXAMPLES::
534
+
535
+ sage: from certlin import *
536
+ sage: Intervals.random(3) # random
537
+ [0, +oo) x (-5, 2) x (0, 1]
538
+ """
539
+ return cls([Interval.random(ring) for _ in range(length)])
540
+
541
+ @classmethod
542
+ def from_bounds(cls, lower_bounds: list, upper_bounds: list, lower_bounds_closed: bool = True, upper_bounds_closed: bool = True) -> Intervals:
543
+ r"""
544
+ Return intervals that are determined by bounds.
545
+
546
+ EXAMPLES::
547
+
548
+ sage: from certlin import *
549
+ sage: Intervals.from_bounds([0, -5, 0], [1, 2, +oo])
550
+ [0, 1] x [-5, 2] x [0, +oo)
551
+ sage: Intervals.from_bounds([0, -5, 0], [1, 2, +oo], False, False)
552
+ (0, 1) x (-5, 2) x (0, +oo)
553
+ sage: Intervals.from_bounds([0, -5, 0], [1, 2, +oo], True, False)
554
+ [0, 1) x [-5, 2) x [0, +oo)
555
+ sage: Intervals.from_bounds([0, -5, 0], [1, 2, +oo], [True, False, True], [True, True, False])
556
+ [0, 1] x (-5, 2] x [0, +oo)
557
+ """
558
+ length = len(lower_bounds)
559
+ if lower_bounds_closed is True:
560
+ lower_bounds_closed = [True] * length
561
+ elif lower_bounds_closed is False:
562
+ lower_bounds_closed = [False] * length
563
+
564
+ if upper_bounds_closed is True:
565
+ upper_bounds_closed = [True] * length
566
+ elif upper_bounds_closed is False:
567
+ upper_bounds_closed = [False] * length
568
+
569
+ return cls([
570
+ Interval(*bounds)
571
+ for bounds in zip(
572
+ lower_bounds, upper_bounds, lower_bounds_closed, upper_bounds_closed
573
+ )
574
+ ])
575
+
576
+ @classmethod
577
+ def from_sign_vector(cls, sv: SignVector) -> Intervals:
578
+ r"""
579
+ Return intervals that are determined by a sign vector.
580
+
581
+ EXAMPLES::
582
+
583
+ sage: from certlin import *
584
+ sage: from sign_vectors import *
585
+ sage: sv = sign_vector("+0-")
586
+ sage: Intervals.from_sign_vector(sv)
587
+ (0, +oo) x {0} x (-oo, 0)
588
+ """
589
+ return cls([
590
+ Interval(
591
+ 0 if element > 0 else (minus_infinity if element < 0 else 0),
592
+ Infinity if element > 0 else (0 if element < 0 else 0),
593
+ element == 0,
594
+ element == 0
595
+ )
596
+ for element in sv
597
+ ])
598
+
599
+ def sign_vectors(self, generator: bool = False) -> list[SignVector] | Iterator[SignVector]:
600
+ r"""
601
+ Compute all sign vectors that correspond to a vector with components in given intervals.
602
+
603
+ INPUT:
604
+
605
+ - ``intervals`` -- a list of intervals
606
+ - ``generator`` -- a boolean (default: ``False``)
607
+
608
+ EXAMPLES::
609
+
610
+ sage: from certlin import *
611
+ sage: intervals = Intervals.from_bounds([-1, 1], [0, 1])
612
+ sage: intervals.sign_vectors()
613
+ [(0+), (-+)]
614
+ sage: intervals = Intervals.from_bounds([-1, -2], [0, 1])
615
+ sage: intervals.sign_vectors()
616
+ [(00), (0+), (0-), (-0), (-+), (--)]
617
+ sage: intervals = Intervals.from_bounds([-1, -1, 0], [0, 5, 0])
618
+ sage: intervals.sign_vectors()
619
+ [(000), (0+0), (0-0), (-00), (-+0), (--0)]
620
+ sage: intervals = Intervals.from_bounds([-1, -1, -1], [0, 1, 0], False, False)
621
+ sage: intervals.sign_vectors()
622
+ [(-0-), (-+-), (---)]
623
+
624
+ TESTS::
625
+
626
+ sage: intervals = Intervals.from_bounds([-1, 0], [1, 0], False, False)
627
+ sage: intervals.sign_vectors()
628
+ []
629
+ sage: intervals = Intervals.from_bounds([], [])
630
+ sage: intervals.sign_vectors()
631
+ []
632
+ """
633
+ list_of_signs = []
634
+ if self.is_empty():
635
+ if generator:
636
+ def empty():
637
+ yield from ()
638
+ return empty()
639
+ return []
640
+ for interval in self:
641
+ available_signs = []
642
+ if 0 in interval:
643
+ available_signs.append(0)
644
+ if interval.supremum() > 0:
645
+ available_signs.append(1)
646
+ if interval.infimum() < 0:
647
+ available_signs.append(-1)
648
+ list_of_signs.append(available_signs)
649
+
650
+ if generator:
651
+ return (
652
+ sign_vector(signs) for signs in cartesian_product_iterator(list_of_signs)
653
+ )
654
+ return [sign_vector(signs) for signs in cartesian_product_iterator(list_of_signs)]