certlin 1.2__tar.gz → 1.4__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: certlin
3
- Version: 1.2
3
+ Version: 1.4
4
4
  Summary: SageMath package for linear inequality systems and certifying (un)solvability
5
5
  Home-page: https://github.com/MarcusAichmayr/certlin
6
6
  Author: Marcus S. Aichmayr
@@ -14,7 +14,7 @@ Classifier: Intended Audience :: Science/Research
14
14
  Classifier: Topic :: Scientific/Engineering :: Mathematics
15
15
  Description-Content-Type: text/markdown
16
16
  License-File: LICENSE
17
- Requires-Dist: elementary_vectors>=2.2
17
+ Requires-Dist: elementary_vectors>=2.4
18
18
  Provides-Extra: passagemath
19
19
  Requires-Dist: passagemath-symbolics; extra == "passagemath"
20
20
  Requires-Dist: passagemath-flint; extra == "passagemath"
@@ -33,7 +33,7 @@ Dynamic: provides-extra
33
33
  Dynamic: requires-dist
34
34
  Dynamic: summary
35
35
 
36
- # Certlin
36
+ # CertLin
37
37
 
38
38
  ## Description
39
39
 
@@ -50,6 +50,8 @@ LICENSE file), either version 3 or (at your option) any later version
50
50
 
51
51
  Sage 10.0 or later is recommended.
52
52
 
53
+ The package [elementary_vectors](https://github.com/MarcusAichmayr/elementary_vectors) is required.
54
+
53
55
  ## Installation
54
56
 
55
57
  ### Install from GitHub (recommended)
@@ -91,7 +93,7 @@ Change to the root directory of the repository and run:
91
93
 
92
94
  The documentation of this package is available on GitHub:
93
95
 
94
- https://marcusaichmayr.github.io/certlin/index.html
96
+ https://marcusaichmayr.github.io/certlin/
95
97
 
96
98
  To generate it, run
97
99
 
@@ -1,4 +1,4 @@
1
- # Certlin
1
+ # CertLin
2
2
 
3
3
  ## Description
4
4
 
@@ -15,6 +15,8 @@ LICENSE file), either version 3 or (at your option) any later version
15
15
 
16
16
  Sage 10.0 or later is recommended.
17
17
 
18
+ The package [elementary_vectors](https://github.com/MarcusAichmayr/elementary_vectors) is required.
19
+
18
20
  ## Installation
19
21
 
20
22
  ### Install from GitHub (recommended)
@@ -56,7 +58,7 @@ Change to the root directory of the repository and run:
56
58
 
57
59
  The documentation of this package is available on GitHub:
58
60
 
59
- https://marcusaichmayr.github.io/certlin/index.html
61
+ https://marcusaichmayr.github.io/certlin/
60
62
 
61
63
  To generate it, run
62
64
 
@@ -1,7 +1,7 @@
1
1
  r"""Certifying linear inequality systems"""
2
2
 
3
3
  #############################################################################
4
- # Copyright (C) 2025 #
4
+ # Copyright (C) 2026 #
5
5
  # Marcus S. Aichmayr (aichmayr@mathematik.uni-kassel.de) #
6
6
  # #
7
7
  # Distributed under the terms of the GNU General Public License (GPL) #
@@ -4,7 +4,7 @@ Intervals
4
4
  """
5
5
 
6
6
  #############################################################################
7
- # Copyright (C) 2025 #
7
+ # Copyright (C) 2026 #
8
8
  # Marcus S. Aichmayr (aichmayr@mathematik.uni-kassel.de) #
9
9
  # #
10
10
  # Distributed under the terms of the GNU General Public License (GPL) #
@@ -108,7 +108,7 @@ class Interval(SageObject):
108
108
  return "{}"
109
109
  if self.is_pointed():
110
110
  return f"{{{self.lower}}}"
111
- 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 ')'}"
111
+ return f"{'[' if self.lower_closed else '('}{'-oo' if self._is_lower_infinite() else self.lower}, {'+oo' if self._is_upper_infinite() else self.upper}{']' if self.upper_closed else ')'}"
112
112
 
113
113
  def _latex_(self) -> str:
114
114
  r"""
@@ -135,9 +135,9 @@ class Interval(SageObject):
135
135
  return r"\{" + f"{self.lower}" + r"\}"
136
136
  return (
137
137
  ("[" if self.lower_closed else "(")
138
- + (r"-\infty" if self.lower == minus_infinity else f"{self.lower}")
138
+ + (r"-\infty" if self._is_lower_infinite() else f"{self.lower}")
139
139
  + ", "
140
- + (r"\infty" if self.upper == Infinity else f"{self.upper}")
140
+ + (r"\infty" if self._is_upper_infinite() else f"{self.upper}")
141
141
  + ("]" if self.upper_closed else ")")
142
142
  )
143
143
 
@@ -158,7 +158,13 @@ class Interval(SageObject):
158
158
 
159
159
  def is_unbounded(self) -> bool:
160
160
  r"""Return whether the interval is unbounded."""
161
- return self.lower == minus_infinity or self.upper == Infinity
161
+ return self._is_lower_infinite() or self._is_upper_infinite()
162
+
163
+ def _is_lower_infinite(self) -> bool:
164
+ return self.lower == minus_infinity
165
+
166
+ def _is_upper_infinite(self) -> bool:
167
+ return self.upper == Infinity
162
168
 
163
169
  def is_bounded(self) -> bool:
164
170
  r"""Return whether the interval is bounded."""
@@ -251,8 +257,8 @@ class Interval(SageObject):
251
257
  return self.upper
252
258
  if self.is_bounded():
253
259
  return (self.lower + self.upper) / 2
254
- if self.lower == minus_infinity:
255
- if self.upper == Infinity:
260
+ if self._is_lower_infinite():
261
+ if self._is_upper_infinite():
256
262
  return 0
257
263
  return self.upper - 1
258
264
  return self.lower + 1
@@ -297,10 +303,10 @@ class Interval(SageObject):
297
303
  if 0 in self:
298
304
  return 0
299
305
  if self.upper - self.lower > 1 or floor(self.lower) + 1 in self or ceil(self.upper) - 1 in self:
300
- if self.lower == minus_infinity:
306
+ if self._is_lower_infinite():
301
307
  floor_upper = floor(self.upper)
302
308
  return floor_upper if floor_upper in self else floor_upper - 1
303
- if self.upper == Infinity:
309
+ if self._is_upper_infinite():
304
310
  ceil_lower = ceil(self.lower)
305
311
  return ceil_lower if ceil_lower in self else ceil_lower + 1
306
312
  if self.lower == 0:
@@ -458,6 +464,9 @@ class Intervals(SageObject):
458
464
  def __getitem__(self, i: int) -> Interval:
459
465
  return self.intervals[i]
460
466
 
467
+ def __setitem__(self, i: int, interval: Interval) -> None:
468
+ self.intervals[i] = interval
469
+
461
470
  def _repr_(self) -> str:
462
471
  if len(self) == 0:
463
472
  return "()"
@@ -537,7 +546,7 @@ class Intervals(SageObject):
537
546
  return cls([Interval.random(ring) for _ in range(length)])
538
547
 
539
548
  @classmethod
540
- def from_bounds(cls, lower_bounds: list, upper_bounds: list, lower_bounds_closed: bool = True, upper_bounds_closed: bool = True) -> Intervals:
549
+ def from_bounds(cls, lower: list, upper: list, lower_closed: bool = True, upper_closed: bool = True) -> Intervals:
541
550
  r"""
542
551
  Return intervals that are determined by bounds.
543
552
 
@@ -553,20 +562,15 @@ class Intervals(SageObject):
553
562
  sage: Intervals.from_bounds([0, -5, 0], [1, 2, +oo], [True, False, True], [True, True, False])
554
563
  [0, 1] x (-5, 2] x [0, +oo)
555
564
  """
556
- length = len(lower_bounds)
557
- if lower_bounds_closed is True:
558
- lower_bounds_closed = [True] * length
559
- elif lower_bounds_closed is False:
560
- lower_bounds_closed = [False] * length
561
-
562
- if upper_bounds_closed is True:
563
- upper_bounds_closed = [True] * length
564
- elif upper_bounds_closed is False:
565
- upper_bounds_closed = [False] * length
566
-
567
- return cls([
568
- Interval(*bounds)
569
- for bounds in zip(
570
- lower_bounds, upper_bounds, lower_bounds_closed, upper_bounds_closed
571
- )
572
- ])
565
+ length = len(lower)
566
+ if lower_closed is True:
567
+ lower_closed = [True] * length
568
+ elif lower_closed is False:
569
+ lower_closed = [False] * length
570
+
571
+ if upper_closed is True:
572
+ upper_closed = [True] * length
573
+ elif upper_closed is False:
574
+ upper_closed = [False] * length
575
+
576
+ return cls([Interval(*bounds) for bounds in zip(lower, upper, lower_closed, upper_closed)])
@@ -7,7 +7,7 @@ with methods to find solutions or to certify their nonexistence.
7
7
  """
8
8
 
9
9
  #############################################################################
10
- # Copyright (C) 2025 #
10
+ # Copyright (C) 2026 #
11
11
  # Marcus S. Aichmayr (aichmayr@mathematik.uni-kassel.de) #
12
12
  # #
13
13
  # Distributed under the terms of the GNU General Public License (GPL) #
@@ -28,7 +28,7 @@ from sage.modules.free_module_element import vector, zero_vector
28
28
  from sage.rings.infinity import Infinity
29
29
  from sage.structure.sage_object import SageObject
30
30
 
31
- from elementary_vectors import ElementaryVectors
31
+ from elementary_vectors import CircuitEnumerator
32
32
  from . import Intervals, Interval
33
33
  from .utility import CombinationsIncluding, solve_without_division
34
34
 
@@ -56,10 +56,10 @@ class LinearInequalitySystem(SageObject):
56
56
  [2, 5) x [5, +oo) x (0, 8) x (-oo, 5]
57
57
  sage: S = LinearInequalitySystem(M, I)
58
58
  sage: S
59
- [1 0] x in [2, 5)
60
- [0 1] x in [5, +oo)
61
- [1 1] x in (0, 8)
62
- [0 1] x in (-oo, 5]
59
+ [1 0] x in [2, 5)
60
+ [0 1] x in [5, +oo)
61
+ [1 1] x in (0, 8)
62
+ [0 1] x in (-oo, 5]
63
63
 
64
64
  We check for solvability::
65
65
 
@@ -69,14 +69,14 @@ class LinearInequalitySystem(SageObject):
69
69
  (5/2, 5)
70
70
  sage: S.find_solution_random() # random
71
71
  (5/2, 5)
72
- sage: S.certify_nonexistence()
72
+ sage: S.certify_unsolvability()
73
73
  Traceback (most recent call last):
74
74
  ...
75
- ValueError: A solution exists!
76
- sage: S.certify_nonexistence_random()
75
+ ValueError: A solution exists.
76
+ sage: S.certify_unsolvability_random()
77
77
  Traceback (most recent call last):
78
78
  ...
79
- MaxIterationsReachedError: Reached maximum number of iterations! Does a solution exist?
79
+ MaxIterationsReachedError: Maximum number of iterations reached. A solution might exist.
80
80
 
81
81
  We rewrite the system as a homogeneous and an inhomogeneous system::
82
82
 
@@ -113,14 +113,14 @@ class LinearInequalitySystem(SageObject):
113
113
  """
114
114
  def __init__(self, matrix: Matrix, intervals: Intervals = None) -> None:
115
115
  if intervals is not None and matrix.nrows() != len(intervals):
116
- raise ValueError("Matrix row count and number of intervals must agree!")
116
+ raise ValueError("Matrix row count and number of intervals must agree.")
117
117
  self._matrix = matrix
118
118
  self._intervals = intervals
119
- self._evs = ElementaryVectors(self.matrix.T)
119
+ self._evs = CircuitEnumerator(self.matrix.T)
120
120
 
121
121
  def _repr_(self) -> str:
122
122
  return "\n".join(
123
- f"{row_str} x in {interval}"
123
+ f"{row_str} x in {interval}"
124
124
  for row_str, interval in zip(str(self.matrix).splitlines(), self.intervals)
125
125
  )
126
126
 
@@ -272,7 +272,7 @@ class LinearInequalitySystem(SageObject):
272
272
  return False
273
273
  return True
274
274
 
275
- def certify_nonexistence(self, iteration_limit: int = -1) -> vector:
275
+ def certify_unsolvability(self, iteration_limit: int = -1) -> vector:
276
276
  r"""
277
277
  Certify nonexistence of a solution if no solution exists.
278
278
 
@@ -291,11 +291,11 @@ class LinearInequalitySystem(SageObject):
291
291
  .. SEEALSO::
292
292
 
293
293
  * :meth:`certify`
294
- * :meth:`certify_nonexistence_random`
294
+ * :meth:`certify_unsolvability_random`
295
295
  """
296
- return self._certify_nonexistence(random=False, reverse=True, iteration_limit=iteration_limit, stop_event=None)
296
+ return self._certify_unsolvability(random=False, reverse=True, iteration_limit=iteration_limit, stop_event=None)
297
297
 
298
- def certify_nonexistence_random(self, iteration_limit: int = 10000) -> vector:
298
+ def certify_unsolvability_random(self, iteration_limit: int = 10000) -> vector:
299
299
  r"""
300
300
  Certify nonexistence of a solution using random elementary vectors.
301
301
 
@@ -315,11 +315,11 @@ class LinearInequalitySystem(SageObject):
315
315
  .. SEEALSO::
316
316
 
317
317
  * :meth:`certify`
318
- * :meth:`certify_nonexistence`
318
+ * :meth:`certify_unsolvability`
319
319
  """
320
- return self._certify_nonexistence(random=True, reverse=False, iteration_limit=iteration_limit, stop_event=None)
320
+ return self._certify_unsolvability(random=True, reverse=False, iteration_limit=iteration_limit, stop_event=None)
321
321
 
322
- def _certify_nonexistence(self, random: bool, reverse: bool, iteration_limit: int, stop_event) -> vector:
322
+ def _certify_unsolvability(self, random: bool, reverse: bool, iteration_limit: int, stop_event) -> vector:
323
323
  r"""
324
324
  Certify nonexistence of a solution.
325
325
 
@@ -333,12 +333,12 @@ class LinearInequalitySystem(SageObject):
333
333
  if stop_event is not None and stop_event.is_set():
334
334
  raise ProcessStoppedError("Process was stopped because another process found a solution.")
335
335
  if iteration_limit != -1 and i >= iteration_limit:
336
- raise MaxIterationsReachedError("Reached maximum number of iterations! Does a solution exist?")
336
+ raise MaxIterationsReachedError("Maximum number of iterations reached. A solution might exist.")
337
337
  if v is None:
338
338
  continue
339
339
  if not self._exists_orthogonal_vector(v):
340
340
  return v
341
- raise ValueError("A solution exists!")
341
+ raise ValueError("A solution exists.")
342
342
 
343
343
  def find_solution(self, iteration_limit: int = -1) -> vector:
344
344
  r"""
@@ -408,8 +408,8 @@ class LinearInequalitySystem(SageObject):
408
408
 
409
409
  .. SEEALSO::
410
410
 
411
- * :meth:`certify_nonexistence`
412
- * :meth:`certify_nonexistence_random`
411
+ * :meth:`certify_unsolvability`
412
+ * :meth:`certify_unsolvability_random`
413
413
  * :meth:`find_solution`
414
414
  * :meth:`find_solution_random`
415
415
  """
@@ -420,7 +420,7 @@ class LinearInequalitySystem(SageObject):
420
420
  stop_event = Manager().Event()
421
421
  with ProcessPoolExecutor(max_workers=2) as executor:
422
422
  futures = {
423
- executor.submit(self._certify_nonexistence, random=random, reverse=True, iteration_limit=iteration_limit, stop_event=stop_event): False,
423
+ executor.submit(self._certify_unsolvability, random=random, reverse=True, iteration_limit=iteration_limit, stop_event=stop_event): False,
424
424
  executor.submit(self._find_solution, random=random, reverse=False, iteration_limit=iteration_limit, stop_event=stop_event): True,
425
425
  }
426
426
  for future in as_completed(futures):
@@ -555,7 +555,7 @@ class HomogeneousSystem(LinearInequalitySystem):
555
555
  if stop_event is not None and stop_event.is_set():
556
556
  raise ProcessStoppedError("Process was stopped because another process found a certificate for nonexistence.")
557
557
  if iteration_limit != -1 and i >= iteration_limit:
558
- raise MaxIterationsReachedError("Reached maximum number of iterations! Is system unsolvable?")
558
+ raise MaxIterationsReachedError("Maximum number of iterations reached. A solution may not exist.")
559
559
  if v is None:
560
560
  continue
561
561
  for w in [v, -v]:
@@ -568,7 +568,7 @@ class HomogeneousSystem(LinearInequalitySystem):
568
568
  return certificate
569
569
  break
570
570
 
571
- raise ValueError("No solution exists!")
571
+ raise ValueError("No solution exists.")
572
572
 
573
573
 
574
574
  class InhomogeneousSystem(LinearInequalitySystem):
@@ -600,9 +600,9 @@ class InhomogeneousSystem(LinearInequalitySystem):
600
600
  (False, (1, 0, 1))
601
601
  sage: S.certify(random=True)
602
602
  (False, (1, 0, 1))
603
- sage: S.certify_nonexistence()
603
+ sage: S.certify_unsolvability()
604
604
  (1, 0, 1)
605
- sage: S.certify_nonexistence_random() # random
605
+ sage: S.certify_unsolvability_random() # random
606
606
  (1, 0, 1)
607
607
 
608
608
  Therefore, we cannot find a solution::
@@ -610,11 +610,11 @@ class InhomogeneousSystem(LinearInequalitySystem):
610
610
  sage: S.find_solution()
611
611
  Traceback (most recent call last):
612
612
  ...
613
- ValueError: No solution exists!
613
+ ValueError: No solution exists.
614
614
  sage: S.find_solution_random()
615
615
  Traceback (most recent call last):
616
616
  ...
617
- MaxIterationsReachedError: Reached maximum number of iterations! Is system unsolvable?
617
+ MaxIterationsReachedError: Maximum number of iterations reached. A solution may not exist.
618
618
 
619
619
  We compute the dual system and write it in homogeneous form::
620
620
 
@@ -4,7 +4,7 @@ Utility functions
4
4
  """
5
5
 
6
6
  #############################################################################
7
- # Copyright (C) 2025 #
7
+ # Copyright (C) 2026 #
8
8
  # Marcus S. Aichmayr (aichmayr@mathematik.uni-kassel.de) #
9
9
  # #
10
10
  # Distributed under the terms of the GNU General Public License (GPL) #
@@ -21,7 +21,7 @@ from sage.matrix.constructor import Matrix
21
21
  from sage.modules.free_module_element import vector
22
22
  from sage.structure.sage_object import SageObject
23
23
 
24
- from elementary_vectors.functions import circuit_generator
24
+ from elementary_vectors.elements import circuit_generator
25
25
 
26
26
 
27
27
  def solve_without_division(matrix: Matrix, rhs: vector) -> vector:
@@ -68,12 +68,12 @@ def solve_without_division(matrix: Matrix, rhs: vector) -> vector:
68
68
  sage: solve_without_division(M, rhs)
69
69
  Traceback (most recent call last):
70
70
  ...
71
- ValueError: No circuit with nonzero last component found. Is there a solution?
71
+ ValueError: No circuit with nonzero last component found. A solution might exist.
72
72
  """
73
73
  for circuit in circuit_generator(Matrix.block([[matrix, Matrix.column(rhs)]]), reverse=True):
74
74
  if circuit[-1] != 0:
75
75
  return -sign(circuit[-1]) * circuit[:-1]
76
- raise ValueError("No circuit with nonzero last component found. Is there a solution?")
76
+ raise ValueError("No circuit with nonzero last component found. A solution might exist.")
77
77
 
78
78
 
79
79
  class CombinationsIncluding(SageObject):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: certlin
3
- Version: 1.2
3
+ Version: 1.4
4
4
  Summary: SageMath package for linear inequality systems and certifying (un)solvability
5
5
  Home-page: https://github.com/MarcusAichmayr/certlin
6
6
  Author: Marcus S. Aichmayr
@@ -14,7 +14,7 @@ Classifier: Intended Audience :: Science/Research
14
14
  Classifier: Topic :: Scientific/Engineering :: Mathematics
15
15
  Description-Content-Type: text/markdown
16
16
  License-File: LICENSE
17
- Requires-Dist: elementary_vectors>=2.2
17
+ Requires-Dist: elementary_vectors>=2.4
18
18
  Provides-Extra: passagemath
19
19
  Requires-Dist: passagemath-symbolics; extra == "passagemath"
20
20
  Requires-Dist: passagemath-flint; extra == "passagemath"
@@ -33,7 +33,7 @@ Dynamic: provides-extra
33
33
  Dynamic: requires-dist
34
34
  Dynamic: summary
35
35
 
36
- # Certlin
36
+ # CertLin
37
37
 
38
38
  ## Description
39
39
 
@@ -50,6 +50,8 @@ LICENSE file), either version 3 or (at your option) any later version
50
50
 
51
51
  Sage 10.0 or later is recommended.
52
52
 
53
+ The package [elementary_vectors](https://github.com/MarcusAichmayr/elementary_vectors) is required.
54
+
53
55
  ## Installation
54
56
 
55
57
  ### Install from GitHub (recommended)
@@ -91,7 +93,7 @@ Change to the root directory of the repository and run:
91
93
 
92
94
  The documentation of this package is available on GitHub:
93
95
 
94
- https://marcusaichmayr.github.io/certlin/index.html
96
+ https://marcusaichmayr.github.io/certlin/
95
97
 
96
98
  To generate it, run
97
99
 
@@ -1,4 +1,4 @@
1
- elementary_vectors>=2.2
1
+ elementary_vectors>=2.4
2
2
 
3
3
  [passagemath]
4
4
  passagemath-symbolics
@@ -9,7 +9,7 @@ def readfile(filename):
9
9
 
10
10
  setup(
11
11
  name="certlin",
12
- version="1.2",
12
+ version="1.4",
13
13
  description="SageMath package for linear inequality systems and certifying (un)solvability",
14
14
  long_description=readfile("README.md"),
15
15
  long_description_content_type="text/markdown",
@@ -34,7 +34,7 @@ setup(
34
34
  "vectors",
35
35
  ],
36
36
  packages=["certlin",],
37
- install_requires=["elementary_vectors>=2.2"],
37
+ install_requires=["elementary_vectors>=2.4"],
38
38
  extras_require={
39
39
  "passagemath": [
40
40
  "passagemath-symbolics",
File without changes
File without changes
File without changes