From 4648c62be2bc36ff41ceb4ead3c0bb40b292ec96 Mon Sep 17 00:00:00 2001
From: ComboProblem <102884863+ComboProblem@users.noreply.github.com>
Date: Thu, 21 Aug 2025 18:12:20 -0700
Subject: [PATCH 01/37] add _partial_eval method to ParametricRealField
---
.../igp/parametric.sage | 22 +++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/cutgeneratingfunctionology/igp/parametric.sage b/cutgeneratingfunctionology/igp/parametric.sage
index 908b10e9a..b5bd9da43 100644
--- a/cutgeneratingfunctionology/igp/parametric.sage
+++ b/cutgeneratingfunctionology/igp/parametric.sage
@@ -562,6 +562,28 @@ class ParametricRealField(Field):
except TypeError: # 'None' components
pass
raise FactorUndetermined("{} cannot be evaluated because the test point is not complete".format(fac))
+
+ def _partial_eval_factor(self, fac):
+ """
+ Partially evaluate ``fac`` on the test point.
+
+ This function is only intended to be called after ``FactorUndetermined`` is raised from ``_eval_factor``.
+ """
+ val_dict = {sym:val for sym, val zip([symb.sym() for symb in self._gens], self._values) if val is not None}
+ return fac.subs(val_dict)
+
+# Returns a symbolic expression or raises an ``EvaluationSuccessfulFlag``.
+# Receiving an ``EvaluationSuccessfulFlag`` means ``fac`` can be evaluated with the known values of the
+# test point.
+# base_ring = self._sym_field.base_ring()
+# if fac in base_ring:
+# raise EvaluationSuccessfulFlag("{} can be evaluated in the base_ring. Use _eval_factor instead.".format(fac))
+# try:
+# fac(self._values)
+# raise EvaluationSuccessfulFlag("{} can be evaluated with the test point. Use _eval_factor instead.".format(fac))
+# except TypeError:
+# val_dict = {sym:val for sym, val zip([symb.sym() for symb in self._gens], self._values) if val is not None}
+# return fac.subs(val_dict)
def _factor_sign(self, fac):
"""
From dbdfb61139b362e949a79e045d990d1b9b13a6d6 Mon Sep 17 00:00:00 2001
From: ComboProblem <102884863+ComboProblem@users.noreply.github.com>
Date: Thu, 21 Aug 2025 18:13:22 -0700
Subject: [PATCH 02/37] update doc to design goals for partial test points
---
cutgeneratingfunctionology/igp/parametric.sage | 17 +++++++----------
1 file changed, 7 insertions(+), 10 deletions(-)
diff --git a/cutgeneratingfunctionology/igp/parametric.sage b/cutgeneratingfunctionology/igp/parametric.sage
index b5bd9da43..04f688b69 100644
--- a/cutgeneratingfunctionology/igp/parametric.sage
+++ b/cutgeneratingfunctionology/igp/parametric.sage
@@ -158,25 +158,22 @@ class ParametricRealField(Field):
sage: f[0]*f[1] <= 4
True
- Test-point free mode (limited functionality and MUCH slower because of many more polynoial
- evaluations via libsingular)::
+ Test-point free descriptions can be written which every comparison is assumed to be true.
+ MUCH slower because of many more polynoial evaluations via libsingular::
sage: K. = ParametricRealField(None, mutable_values=True)
sage: a <= 2
- Traceback (most recent call last):
- ...
- FactorUndetermined: a cannot be evaluated because the test point is not complete
+ True
sage: K.assume_comparison(a.sym(), operator.le, 3)
- Partial test point mode::
+ Comparisons with test-points that are partially defined are supported. Comparisons made in
+ unspecified variables are assumed to be true::
sage: K. = ParametricRealField([None, 1], mutable_values=True)
sage: a <= 2
- Traceback (most recent call last):
- ...
- FactorUndetermined: a cannot be evaluated because the test point is not complete
- sage: b <= 11
True
+ sage: b <= 11
+ True
"""
Element = ParametricRealFieldElement
From e47fcd5f5891eb6a8fdd88fbb9096109519e8f9c Mon Sep 17 00:00:00 2001
From: ComboProblem <102884863+ComboProblem@users.noreply.github.com>
Date: Thu, 21 Aug 2025 18:18:16 -0700
Subject: [PATCH 03/37] change assume_comparison to support partial evaluations
---
cutgeneratingfunctionology/igp/parametric.sage | 15 ++++++++++++---
1 file changed, 12 insertions(+), 3 deletions(-)
diff --git a/cutgeneratingfunctionology/igp/parametric.sage b/cutgeneratingfunctionology/igp/parametric.sage
index 04f688b69..7628e7541 100644
--- a/cutgeneratingfunctionology/igp/parametric.sage
+++ b/cutgeneratingfunctionology/igp/parametric.sage
@@ -174,7 +174,6 @@ class ParametricRealField(Field):
True
sage: b <= 11
True
-
"""
Element = ParametricRealFieldElement
@@ -859,14 +858,24 @@ class ParametricRealField(Field):
try:
comparison_val = comparison.val()
except FactorUndetermined:
- comparison_val = None
+ # partial test point evaluation; assume evaluation is true
+ # so record the assumed factor in the BSA
+ # it is the responsibility of the BSA to know if it is empty or not
+ # most implementations of BSAs cannot do this for non-linear cases.
+ assumed_fac = self._partial_eval_factor(comparison)
+ if not is_factor_known(assumed_fac):
+ record_comparision(assumed_fac, op)
+ return
comparison = comparison.sym()
else:
comparison = self._sym_field(comparison)
try:
comparison_val = self._eval_factor(comparison)
except FactorUndetermined:
- comparison_val = None
+ assumed_fac = self._partial_eval_factor(comparison)
+ if not is_factor_known(assumed_fac):
+ record_comparision(assumed_fac, op)
+ return
if comparison_val is not None:
if not op(comparison_val, 0):
if comparison in base_ring:
From 44cf014f6ea22948f65db308aeda3360da89c65c Mon Sep 17 00:00:00 2001
From: ComboProblem <102884863+ComboProblem@users.noreply.github.com>
Date: Fri, 22 Aug 2025 14:34:21 -0700
Subject: [PATCH 04/37] fix syntax
---
cutgeneratingfunctionology/igp/parametric.sage | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/cutgeneratingfunctionology/igp/parametric.sage b/cutgeneratingfunctionology/igp/parametric.sage
index 7628e7541..78d96b260 100644
--- a/cutgeneratingfunctionology/igp/parametric.sage
+++ b/cutgeneratingfunctionology/igp/parametric.sage
@@ -565,7 +565,9 @@ class ParametricRealField(Field):
This function is only intended to be called after ``FactorUndetermined`` is raised from ``_eval_factor``.
"""
- val_dict = {sym:val for sym, val zip([symb.sym() for symb in self._gens], self._values) if val is not None}
+ syms = [symb.sym() for symb in self._gens]
+ val_dict = {sym:val for sym, val in zip(syms , self._values) if val is not None}
+ print(val_dict)
return fac.subs(val_dict)
# Returns a symbolic expression or raises an ``EvaluationSuccessfulFlag``.
From 96f0921d0bf645da3a8dc55bd623d500579e1d56 Mon Sep 17 00:00:00 2001
From: ComboProblem <102884863+ComboProblem@users.noreply.github.com>
Date: Fri, 22 Aug 2025 14:52:21 -0700
Subject: [PATCH 05/37] move FactorUndetermined to its own file to make it a
shared resouce
---
cutgeneratingfunctionology/igp/parametric.sage | 4 +---
cutgeneratingfunctionology/shared/EvaluationExceptions.py | 2 ++
.../spam/parametric_real_field_element.py | 3 ++-
3 files changed, 5 insertions(+), 4 deletions(-)
create mode 100644 cutgeneratingfunctionology/shared/EvaluationExceptions.py
diff --git a/cutgeneratingfunctionology/igp/parametric.sage b/cutgeneratingfunctionology/igp/parametric.sage
index 78d96b260..8facd9031 100644
--- a/cutgeneratingfunctionology/igp/parametric.sage
+++ b/cutgeneratingfunctionology/igp/parametric.sage
@@ -20,6 +20,7 @@ from cutgeneratingfunctionology.spam.basic_semialgebraic_local import BasicSemia
from cutgeneratingfunctionology.spam.semialgebraic_mathematica import BasicSemialgebraicSet_mathematica, from_mathematica
from cutgeneratingfunctionology.spam.basic_semialgebraic_groebner_basis import BasicSemialgebraicSet_groebner_basis
from cutgeneratingfunctionology.spam.polyhedral_complex import PolyhedralComplex
+from cutgeneratingfunctionology.shared.EvaluationExceptions import FactorUndetermined
from .parametric_family import Classcall, ParametricFamily_base, ParametricFamily
debug_new_factors = False
@@ -58,9 +59,6 @@ class ParametricRealFieldRefinementError(ValueError):
from contextlib import contextmanager
-class FactorUndetermined(Exception):
- pass
-
allow_refinement_default = True
big_cells_default = 'if_not_allow_refinement'
mutable_values_default = False
diff --git a/cutgeneratingfunctionology/shared/EvaluationExceptions.py b/cutgeneratingfunctionology/shared/EvaluationExceptions.py
new file mode 100644
index 000000000..7e7dd6e04
--- /dev/null
+++ b/cutgeneratingfunctionology/shared/EvaluationExceptions.py
@@ -0,0 +1,2 @@
+class FactorUndetermined(Exception): # FactorUndetermined is raised when an expression can not be evaluated with a test point.
+ pass
\ No newline at end of file
diff --git a/cutgeneratingfunctionology/spam/parametric_real_field_element.py b/cutgeneratingfunctionology/spam/parametric_real_field_element.py
index c6688eeeb..40c1a4ea9 100644
--- a/cutgeneratingfunctionology/spam/parametric_real_field_element.py
+++ b/cutgeneratingfunctionology/spam/parametric_real_field_element.py
@@ -9,6 +9,7 @@
from sage.rings.real_mpfr import RR
from sage.functions.other import ceil, floor
from sage.functions.generalized import sign
+from cutgeneratingfunctionology.shared.EvaluationExceptions import FactorUndetermined
import operator
def richcmp_op_negation(op):
@@ -20,7 +21,7 @@ def richcmp_op_negation(op):
return op_NE
elif op == op_NE:
return op_EQ
- elif op == op_GT:
+ elif op == op_GT:s
return op_LE
elif op == op_GE:
return op_LT
From 387f17799fa34c8ef917fce9943c8d1ffecdc015 Mon Sep 17 00:00:00 2001
From: ComboProblem <102884863+ComboProblem@users.noreply.github.com>
Date: Thu, 28 Aug 2025 15:13:15 -0700
Subject: [PATCH 06/37] inprogress
---
.../igp/parametric.sage | 30 +++++++++++--------
.../spam/parametric_real_field_element.py | 17 +++++++----
2 files changed, 30 insertions(+), 17 deletions(-)
diff --git a/cutgeneratingfunctionology/igp/parametric.sage b/cutgeneratingfunctionology/igp/parametric.sage
index 8facd9031..4dbc7b660 100644
--- a/cutgeneratingfunctionology/igp/parametric.sage
+++ b/cutgeneratingfunctionology/igp/parametric.sage
@@ -563,9 +563,7 @@ class ParametricRealField(Field):
This function is only intended to be called after ``FactorUndetermined`` is raised from ``_eval_factor``.
"""
- syms = [symb.sym() for symb in self._gens]
- val_dict = {sym:val for sym, val in zip(syms , self._values) if val is not None}
- print(val_dict)
+ val_dict = {sym:val for sym, val in zip(fac.parent().gens() , self._values) if val is not None}
return fac.subs(val_dict)
# Returns a symbolic expression or raises an ``EvaluationSuccessfulFlag``.
@@ -858,14 +856,21 @@ class ParametricRealField(Field):
try:
comparison_val = comparison.val()
except FactorUndetermined:
- # partial test point evaluation; assume evaluation is true
- # so record the assumed factor in the BSA
- # it is the responsibility of the BSA to know if it is empty or not
- # most implementations of BSAs cannot do this for non-linear cases.
+ # Partial test point evaluation assumes the partially
+ # evaluated factor is True.
+ # So, we record the assumed factor in the BSA without checking if the factor
+ # should be addeded or not.
+ # It becomes the responsibility of the BSA to detemined if the recorded factors
+ # so far repersent a non-empty BSA.
+ # Most implementations of BSAs cannot do this for non-linear cases.
+ # With a BSA that is equipped with a first order logic solver like QPEAD
+ # should be able to do this.
assumed_fac = self._partial_eval_factor(comparison)
- if not is_factor_known(assumed_fac):
- record_comparision(assumed_fac, op)
- return
+ self.record_factor(assumed_fac, op)
+ print(assumed_fac)
+ if self._bsa.is_empty():
+ raise ParametricRealFieldInconsistencyError("Assumed constraint {} derivied from the comparision {} {} {} is inconsistent with already recoreded constraints".format(assumed_fac, lhs, op, rhs))
+ return
comparison = comparison.sym()
else:
comparison = self._sym_field(comparison)
@@ -873,8 +878,9 @@ class ParametricRealField(Field):
comparison_val = self._eval_factor(comparison)
except FactorUndetermined:
assumed_fac = self._partial_eval_factor(comparison)
- if not is_factor_known(assumed_fac):
- record_comparision(assumed_fac, op)
+ if not self.is_factor_known(assumed_fac, op):
+ self.record_factor(assumed_fac, op)
+ print("here", assumed_fac)
return
if comparison_val is not None:
if not op(comparison_val, 0):
diff --git a/cutgeneratingfunctionology/spam/parametric_real_field_element.py b/cutgeneratingfunctionology/spam/parametric_real_field_element.py
index 40c1a4ea9..99d6a82fc 100644
--- a/cutgeneratingfunctionology/spam/parametric_real_field_element.py
+++ b/cutgeneratingfunctionology/spam/parametric_real_field_element.py
@@ -21,7 +21,7 @@ def richcmp_op_negation(op):
return op_NE
elif op == op_NE:
return op_EQ
- elif op == op_GT:s
+ elif op == op_GT:
return op_LE
elif op == op_GE:
return op_LT
@@ -77,9 +77,13 @@ def sym(self):
def val(self):
try:
return self._val
- except AttributeError:
- return self.parent()._eval_factor(self._sym)
-
+ except AttributeError: # with imutable values, this fales because we get some hash map weirdness
+ try:
+ return self.parent()._eval_factor(self._sym)
+ except FactorUndetermined:
+ possible_val = self.parent()._partial_eval_factor(self._sym)
+ if possible_val in possible_val.base_ring():
+ return possible_val
def _richcmp_(left, right, op):
r"""
Examples for traditional cmp semantics::
@@ -127,7 +131,10 @@ def _richcmp_(left, right, op):
# shouldn't really happen, within coercion
raise TypeError("comparing elements from different fields")
if left.parent()._big_cells:
- result = richcmp(left.val(), right.val(), op)
+ try:
+ result = richcmp(left.val(), right.val(), op)
+ except FactorUndetermined:
+ result = True
if result:
true_op = op
else:
From 56baa49e5eb3edc6f25bb93f81ec3ff670edbb89 Mon Sep 17 00:00:00 2001
From: ComboProblem <102884863+ComboProblem@users.noreply.github.com>
Date: Sat, 30 Aug 2025 10:39:31 -0700
Subject: [PATCH 07/37] working partial test point more, next step fix doc
string tests
---
.../igp/parametric.sage | 31 ++++++-------------
1 file changed, 10 insertions(+), 21 deletions(-)
diff --git a/cutgeneratingfunctionology/igp/parametric.sage b/cutgeneratingfunctionology/igp/parametric.sage
index 4dbc7b660..2ae87bb7f 100644
--- a/cutgeneratingfunctionology/igp/parametric.sage
+++ b/cutgeneratingfunctionology/igp/parametric.sage
@@ -171,7 +171,7 @@ class ParametricRealField(Field):
sage: a <= 2
True
sage: b <= 11
- True
+ True
"""
Element = ParametricRealFieldElement
@@ -856,38 +856,27 @@ class ParametricRealField(Field):
try:
comparison_val = comparison.val()
except FactorUndetermined:
- # Partial test point evaluation assumes the partially
- # evaluated factor is True.
- # So, we record the assumed factor in the BSA without checking if the factor
- # should be addeded or not.
- # It becomes the responsibility of the BSA to detemined if the recorded factors
- # so far repersent a non-empty BSA.
- # Most implementations of BSAs cannot do this for non-linear cases.
- # With a BSA that is equipped with a first order logic solver like QPEAD
- # should be able to do this.
- assumed_fac = self._partial_eval_factor(comparison)
- self.record_factor(assumed_fac, op)
- print(assumed_fac)
- if self._bsa.is_empty():
- raise ParametricRealFieldInconsistencyError("Assumed constraint {} derivied from the comparision {} {} {} is inconsistent with already recoreded constraints".format(assumed_fac, lhs, op, rhs))
- return
+ comparison_val = None
comparison = comparison.sym()
else:
comparison = self._sym_field(comparison)
try:
comparison_val = self._eval_factor(comparison)
except FactorUndetermined:
- assumed_fac = self._partial_eval_factor(comparison)
- if not self.is_factor_known(assumed_fac, op):
- self.record_factor(assumed_fac, op)
- print("here", assumed_fac)
- return
+ comparison_val = None
if comparison_val is not None:
if not op(comparison_val, 0):
if comparison in base_ring:
raise ParametricRealFieldInconsistencyError("New constant constraint {} {} {} is not satisfied".format(lhs, op, rhs))
else:
raise ParametricRealFieldInconsistencyError("New constraint {} {} {} is not satisfied by the test point".format(lhs, op, rhs))
+ else: #A numerical evaluation of the expression has failed. Assume the partial evaluation of the expression holds.
+ comparison_val_or_expr = self._partial_eval_factor(comparison)
+ if not op(comparison_val_or_expr, 0): #The partial evual
+ if comparison_val_or_expr in base_ring:
+ raise ParametricRealFieldInconsistencyError("New constant constraint {} {} {} is not satisfied".format(lhs, op, rhs))
+ else: # comparision_val_or_expr is algebraic expresion, the only "true" comparision here is comparionsion val_or_expr
+ comparison = comparison_val_or_expr
if comparison in base_ring:
return
if comparison.denominator() == 1 and comparison.numerator().degree() == 1:
From 921178f667a152f7845692ec201a9f0cc0ae2999 Mon Sep 17 00:00:00 2001
From: ComboProblem <102884863+ComboProblem@users.noreply.github.com>
Date: Sat, 30 Aug 2025 15:12:41 -0700
Subject: [PATCH 08/37] finish comments; clean up tests.
---
cutgeneratingfunctionology/igp/parametric.sage | 17 ++++++++---------
.../spam/parametric_real_field_element.py | 2 +-
2 files changed, 9 insertions(+), 10 deletions(-)
diff --git a/cutgeneratingfunctionology/igp/parametric.sage b/cutgeneratingfunctionology/igp/parametric.sage
index 2ae87bb7f..631226716 100644
--- a/cutgeneratingfunctionology/igp/parametric.sage
+++ b/cutgeneratingfunctionology/igp/parametric.sage
@@ -162,7 +162,8 @@ class ParametricRealField(Field):
sage: K. = ParametricRealField(None, mutable_values=True)
sage: a <= 2
True
- sage: K.assume_comparison(a.sym(), operator.le, 3)
+ sage: K. = ParametricRealField(None, mutable_values=True)
+ sage: K.assume_comparison(a.sym(), operator.le, 2)
Comparisons with test-points that are partially defined are supported. Comparisons made in
unspecified variables are assumed to be true::
@@ -312,7 +313,7 @@ class ParametricRealField(Field):
sage: sqrt2, = nice_field_values([sqrt(2)])
sage: K. = ParametricRealField([0], base_ring=sqrt2.parent())
sage: f + sqrt2
- (f + (a))~
+ (f + a)~
This currently does not work for Sage's built-in embedded number field elements...
"""
@@ -404,9 +405,7 @@ class ParametricRealField(Field):
....: with K.temporary_assumptions():
....: K.assume_comparison(a.sym(), operator.le, 3)
....: a <= 4
- Traceback (most recent call last):
- ...
- FactorUndetermined: a cannot be evaluated because the test point is not complete...
+ True
"""
self._values = [ None for n in self._names ]
@@ -872,11 +871,11 @@ class ParametricRealField(Field):
raise ParametricRealFieldInconsistencyError("New constraint {} {} {} is not satisfied by the test point".format(lhs, op, rhs))
else: #A numerical evaluation of the expression has failed. Assume the partial evaluation of the expression holds.
comparison_val_or_expr = self._partial_eval_factor(comparison)
- if not op(comparison_val_or_expr, 0): #The partial evual
- if comparison_val_or_expr in base_ring:
+ if comparison_val_or_expr in base_ring:
+ if not op(comparison_val_or_expr, 0): #The partial evaluation is ture, means
raise ParametricRealFieldInconsistencyError("New constant constraint {} {} {} is not satisfied".format(lhs, op, rhs))
- else: # comparision_val_or_expr is algebraic expresion, the only "true" comparision here is comparionsion val_or_expr
- comparison = comparison_val_or_expr
+ else: # comparision_val_or_expr is algebraic expresion, asume the comparision here is comparionsion_val_or_expr
+ comparison = comparison_val_or_expr
if comparison in base_ring:
return
if comparison.denominator() == 1 and comparison.numerator().degree() == 1:
diff --git a/cutgeneratingfunctionology/spam/parametric_real_field_element.py b/cutgeneratingfunctionology/spam/parametric_real_field_element.py
index 99d6a82fc..656734c4a 100644
--- a/cutgeneratingfunctionology/spam/parametric_real_field_element.py
+++ b/cutgeneratingfunctionology/spam/parametric_real_field_element.py
@@ -133,7 +133,7 @@ def _richcmp_(left, right, op):
if left.parent()._big_cells:
try:
result = richcmp(left.val(), right.val(), op)
- except FactorUndetermined:
+ except FactorUndetermined: # Partial evauation is happen, assume the result is True.
result = True
if result:
true_op = op
From 81d9b6cfb19d269274eeceae9f05ad156bcbb977 Mon Sep 17 00:00:00 2001
From: ComboProblem <102884863+ComboProblem@users.noreply.github.com>
Date: Sat, 30 Aug 2025 15:19:42 -0700
Subject: [PATCH 09/37] remove some comments
---
.../spam/parametric_real_field_element.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cutgeneratingfunctionology/spam/parametric_real_field_element.py b/cutgeneratingfunctionology/spam/parametric_real_field_element.py
index 656734c4a..b50c7d8d1 100644
--- a/cutgeneratingfunctionology/spam/parametric_real_field_element.py
+++ b/cutgeneratingfunctionology/spam/parametric_real_field_element.py
@@ -77,7 +77,7 @@ def sym(self):
def val(self):
try:
return self._val
- except AttributeError: # with imutable values, this fales because we get some hash map weirdness
+ except AttributeError:
try:
return self.parent()._eval_factor(self._sym)
except FactorUndetermined:
From 29ae8eb1e33c40137c368e481b660c20ff9f6d7d Mon Sep 17 00:00:00 2001
From: ComboProblem <102884863+ComboProblem@users.noreply.github.com>
Date: Mon, 29 Sep 2025 18:58:27 -0700
Subject: [PATCH 10/37] finish fixing tests
---
.../spam/parametric_real_field_element.py | 43 ++++++++++++++++---
1 file changed, 36 insertions(+), 7 deletions(-)
diff --git a/cutgeneratingfunctionology/spam/parametric_real_field_element.py b/cutgeneratingfunctionology/spam/parametric_real_field_element.py
index b50c7d8d1..39c6375b0 100644
--- a/cutgeneratingfunctionology/spam/parametric_real_field_element.py
+++ b/cutgeneratingfunctionology/spam/parametric_real_field_element.py
@@ -84,6 +84,8 @@ def val(self):
possible_val = self.parent()._partial_eval_factor(self._sym)
if possible_val in possible_val.base_ring():
return possible_val
+ else:
+ raise FactorUndetermined
def _richcmp_(left, right, op):
r"""
Examples for traditional cmp semantics::
@@ -132,7 +134,7 @@ def _richcmp_(left, right, op):
raise TypeError("comparing elements from different fields")
if left.parent()._big_cells:
try:
- result = richcmp(left.val(), right.val(), op)
+ result = richcmp((left-right).val(), 0, op)
except FactorUndetermined: # Partial evauation is happen, assume the result is True.
result = True
if result:
@@ -158,13 +160,40 @@ def _richcmp_(left, right, op):
return result
else:
# Traditional cmp semantics.
- if (left.val() == right.val()):
- left.parent().assume_comparison(right, operator.eq, left)
- elif (left.val() < right.val()):
- left.parent().assume_comparison(left, operator.lt, right)
+ try:
+ expr_val = (left-right).val()
+ if( expr_val == 0):
+ left.parent().assume_comparison(right, operator.eq, left)
+ elif (expr_val < 0):
+ left.parent().assume_comparison(left, operator.lt, right)
+ else:
+ left.parent().assume_comparison(right, operator.lt, left)
+ return richcmp(left.val(), right.val(), op)
+ except FactorUndetermined:
+ result = True
+ if result:
+ true_op = op
+ else:
+ true_op = richcmp_op_negation(op)
+ # left.sym() - right.sym() may cancel denominators, but that is
+ # OK because _div_ makes sure that denominators are nonzero.
+ if true_op == op_LT:
+ left.parent().assume_comparison(left - right, operator.lt)
+ elif true_op == op_GT:
+ left.parent().assume_comparison(left - right, operator.gt)
+ elif true_op == op_EQ:
+ left.parent().assume_comparison(right - left, operator.eq)
+ elif true_op == op_LE:
+ left.parent().assume_comparison(left - right, operator.le)
+ elif true_op == op_GE:
+ left.parent().assume_comparison(left - right, operator.ge)
+ elif true_op == op_NE:
+ left.parent().assume_comparison(right - left, operator.ne)
else:
- left.parent().assume_comparison(right, operator.lt, left)
- return richcmp(left.val(), right.val(), op)
+ raise ValueError("{} is not a valid richcmp operator".format(op))
+ return True
+
+
def __abs__(self):
"""
From a9cf7cc87759667c5140d9a319c3881c29022022 Mon Sep 17 00:00:00 2001
From: ComboProblem <102884863+ComboProblem@users.noreply.github.com>
Date: Tue, 30 Sep 2025 10:07:00 -0700
Subject: [PATCH 11/37] linting
---
.../igp/parametric.sage | 146 +++++++++---------
.../spam/parametric_real_field_element.py | 18 +--
2 files changed, 81 insertions(+), 83 deletions(-)
diff --git a/cutgeneratingfunctionology/igp/parametric.sage b/cutgeneratingfunctionology/igp/parametric.sage
index 631226716..4b0734922 100644
--- a/cutgeneratingfunctionology/igp/parametric.sage
+++ b/cutgeneratingfunctionology/igp/parametric.sage
@@ -37,12 +37,12 @@ def bigcellify_igp():
import cutgeneratingfunctionology.spam.big_cells as big_cells
big_cells.bigcellify_module(igp)
+
###############################
# Parametric Real Number Field
###############################
from cutgeneratingfunctionology.spam.parametric_real_field_element import ParametricRealFieldElement, is_parametric_element
-
from sage.rings.ring import Field
import sage.rings.number_field.number_field_base as number_field_base
from sage.structure.coerce_maps import CallableConvertMap
@@ -51,12 +51,15 @@ from sage.structure.coerce_maps import CallableConvertMap
class ParametricRealFieldFrozenError(ValueError):
pass
+
class ParametricRealFieldInconsistencyError(ValueError):
pass
+
class ParametricRealFieldRefinementError(ValueError):
pass
+
from contextlib import contextmanager
allow_refinement_default = True
@@ -156,7 +159,7 @@ class ParametricRealField(Field):
sage: f[0]*f[1] <= 4
True
- Test-point free descriptions can be written which every comparison is assumed to be true.
+ Test-point free descriptions can be written which every comparison is assumed to be true.
MUCH slower because of many more polynoial evaluations via libsingular::
sage: K. = ParametricRealField(None, mutable_values=True)
@@ -205,7 +208,7 @@ class ParametricRealField(Field):
self._big_cells = big_cells
self._zero_element = ParametricRealFieldElement(self, 0)
- self._one_element = ParametricRealFieldElement(self, 1)
+ self._one_element = ParametricRealFieldElement(self, 1)
## REFACTOR: Maybe replace this by an instance of BasicSemialgebraicSet_eq_lt_le_sets - but careful - this class right now assumes polynomials
self._eq = set([])
self._lt = set([])
@@ -307,7 +310,7 @@ class ParametricRealField(Field):
TypeError: unsupported operand parent(s)...
Test that real number field elements can be upgraded to ``ParametricRealFieldElement``s.
- Note that this requires setting up the ParametricRealField with a specific base ring,
+ Note that this requires setting up the ParametricRealField with a specific base ring,
because there is no common parent of QQ(x) and a RealNumberField``::
sage: sqrt2, = nice_field_values([sqrt(2)])
@@ -319,7 +322,7 @@ class ParametricRealField(Field):
"""
if isinstance(S, ParametricRealField) and self is not S:
return None
- if S is sage.interfaces.mathematica.MathematicaElement or isinstance(S, RealNumberField_absolute) or isinstance(S, RealNumberField_quadratic) or AA.has_coerce_map_from(S):
+ if S is sage.interfaces.mathematica.MathematicaElement or isinstance(S, RealNumberField_absolute) or isinstance(S, RealNumberField_quadratic) or AA.has_coerce_map_from(S):
# Does the test with MathematicaElement actually work?
# We test whether S coerces into AA. This rules out inexact fields such as RDF.
return True
@@ -555,29 +558,16 @@ class ParametricRealField(Field):
except TypeError: # 'None' components
pass
raise FactorUndetermined("{} cannot be evaluated because the test point is not complete".format(fac))
-
+
def _partial_eval_factor(self, fac):
"""
Partially evaluate ``fac`` on the test point.
-
+
This function is only intended to be called after ``FactorUndetermined`` is raised from ``_eval_factor``.
"""
val_dict = {sym:val for sym, val in zip(fac.parent().gens() , self._values) if val is not None}
return fac.subs(val_dict)
-# Returns a symbolic expression or raises an ``EvaluationSuccessfulFlag``.
-# Receiving an ``EvaluationSuccessfulFlag`` means ``fac`` can be evaluated with the known values of the
-# test point.
-# base_ring = self._sym_field.base_ring()
-# if fac in base_ring:
-# raise EvaluationSuccessfulFlag("{} can be evaluated in the base_ring. Use _eval_factor instead.".format(fac))
-# try:
-# fac(self._values)
-# raise EvaluationSuccessfulFlag("{} can be evaluated with the test point. Use _eval_factor instead.".format(fac))
-# except TypeError:
-# val_dict = {sym:val for sym, val zip([symb.sym() for symb in self._gens], self._values) if val is not None}
-# return fac.subs(val_dict)
-
def _factor_sign(self, fac):
"""
Determine the sign of ``fac`` evaluated on the test point.
@@ -870,7 +860,7 @@ class ParametricRealField(Field):
else:
raise ParametricRealFieldInconsistencyError("New constraint {} {} {} is not satisfied by the test point".format(lhs, op, rhs))
else: #A numerical evaluation of the expression has failed. Assume the partial evaluation of the expression holds.
- comparison_val_or_expr = self._partial_eval_factor(comparison)
+ comparison_val_or_expr = self._partial_eval_factor(comparison)
if comparison_val_or_expr in base_ring:
if not op(comparison_val_or_expr, 0): #The partial evaluation is ture, means
raise ParametricRealFieldInconsistencyError("New constant constraint {} {} {} is not satisfied".format(lhs, op, rhs))
@@ -906,7 +896,8 @@ class ParametricRealField(Field):
return
if len(factors) == 1 and factors[0][1] == 1 and comparison_val is not None:
the_fac, d = factors[0]
- the_sign = sign(factors.unit() * comparison_val)
+ the_sign = sign(factors.unit() * comparison_val)
+
def factor_sign(fac):
if fac == the_fac:
return the_sign
@@ -1130,7 +1121,7 @@ class ParametricRealField(Field):
def make_proof_cell(self, **opt):
r"""
Make a :class:`SemialgebraicComplexComponent` from a :class:`ParametricRealField`.
-
+
In **opt, one can provide: region_type, function, find_region_type, default_var_bound, bddbsa, kwds_dict.
EXAMPLES::
@@ -1232,8 +1223,10 @@ def find_polynomial_map(eqs=[], poly_ring=None):
# Functions with ParametricRealField K
######################################
+
from sage.misc.sageinspect import sage_getargspec, sage_getvariablename
+
def read_default_args(function, **opt_non_default):
r"""
Return the default values of arguments of the function.
@@ -1261,7 +1254,7 @@ def read_default_args(function, **opt_non_default):
default_args = {}
if defaults is not None:
for i in range(len(defaults)):
- default_args[args[-i-1]]=defaults[-i-1]
+ default_args[args[-i-1]] = defaults[-i-1]
for (opt_name, opt_value) in opt_non_default.items():
if opt_name in default_args:
default_args[opt_name] = opt_value
@@ -1422,7 +1415,7 @@ class SemialgebraicComplexComponent(SageObject): # FIXME: Rename this to be m
else:
ptcolor = 'white'
if (xmin <= pt[0] <= xmax) and (ymin <= pt[1] <= ymax):
- g += point(pt, color = ptcolor, size = 2, zorder=10)
+ g += point(pt, color=ptcolor, size=2, zorder=10)
return g
def find_neighbour_candidates(self, flip_ineq_step, wall_crossing_method='heuristic', goto_lower_dim=False, pos_poly=None):
@@ -1469,7 +1462,7 @@ class SemialgebraicComplexComponent(SageObject): # FIXME: Rename this to be m
pt = find_point_flip_ineq_heuristic(self.var_value, l, list(bsa.lt_poly())+list(bsa.le_poly()), flip_ineq_step)
if pt is not None:
# Find a new point, use polynomial map to recover the values of those eliminated variables.
- pt_across_wall = tuple(p(pt) for p in self.polynomial_map)
+ pt_across_wall = tuple(p(pt) for p in self.polynomial_map)
if wall_crossing_method == 'mathematica' or wall_crossing_method == 'heuristic_then_mathematica' and (pt_across_wall is None):
bsa_mathematica = bsa.formal_relint(bsa_class='mathematica') # was BasicSemialgebraicSet_mathematica.from_bsa(bsa)
bsa_mathematica.add_polynomial_constraint(l, operator.gt)
@@ -1556,7 +1549,9 @@ class SemialgebraicComplexComponent(SageObject): # FIXME: Rename this to be m
if bsa_section.upstairs()._polyhedron.is_empty():
has_pt_on_wall = False
else:
- lts = []; les = []; eqs = []
+ lts = []
+ les = []
+ eqs = []
for ll in list(bsa_section.lt_poly()):
factors = ll.factor()
if len(factors) == 1:
@@ -1684,7 +1679,7 @@ class ProofCell(SemialgebraicComplexComponent, Classcall):
sage: C_copy._init_args == C._init_args
True
- (We do not test for equality C_copy == C -- we have not even decided yet what the semantics
+ (We do not test for equality C_copy == C -- we have not even decided yet what the semantics
of equality of bsa is.)
"""
@@ -1794,8 +1789,10 @@ class ProofCell(SemialgebraicComplexComponent, Classcall):
super(ProofCell, self).__init__(K, region_type, bddbsa, polynomial_map)
self.family = family
+
from collections import OrderedDict
+
class SemialgebraicComplex(SageObject):
r"""
A proof complex for parameter space analysis.
@@ -1829,7 +1826,7 @@ class SemialgebraicComplex(SageObject):
sage: complex.is_complete() # optional - mathematica
True
-
+
Example with non-linear wall::
sage: complex = SemialgebraicComplex(lambda x,y: max(x,y^2), ['x','y'], find_region_type=result_symbolic_expression, default_var_bound=(-3,3)) # optional - mathematica
@@ -1924,7 +1921,7 @@ class SemialgebraicComplex(SageObject):
to a "type" of the parameter region, for example:
- :func:`find_region_type_igp` (the default). The result of the computation is a Gomory-Johnson
- function `h`; it is passed to :func:`find_region_type_igp` as 2nd arg,
+ function `h`; it is passed to :func:`find_region_type_igp` as 2nd arg,
and then :func:`find_region_type_igp`which classifies the region of the
parameter by returning one of the strings
``'is_constructible'``, ``'not_constructible'``,
@@ -2003,7 +2000,7 @@ class SemialgebraicComplex(SageObject):
r"""
Return a random point that satisfies var_bounds and is in self.bsa.
- - If var_bounds is not specified, self.default_var_bound is taken.
+ - If var_bounds is not specified, self.default_var_bound is taken.
- var_bounds can be a list of 2-tuples whose length equals to the number of parameters, or lambda functions.
- It is used in random shooting method for functions like ``dg_2_step_mir``, which involve floor/ceil operations. We try to plot one layer for each n = floor(...) and superimpose the layers at the end to get the whole picture.
@@ -2029,11 +2026,11 @@ class SemialgebraicComplex(SageObject):
x = QQ(uniform(self.default_var_bound[0], self.default_var_bound[1]))
else:
if hasattr(var_bounds[i][0], '__call__'):
- l = var_bounds[i][0](*var_value)
+ l = var_bounds[i][0](*var_value)
else:
l = var_bounds[i][0]
if hasattr(var_bounds[i][1], '__call__'):
- u = var_bounds[i][1](*var_value)
+ u = var_bounds[i][1](*var_value)
else:
u = var_bounds[i][1]
if l > u:
@@ -2094,12 +2091,12 @@ class SemialgebraicComplex(SageObject):
for c in self.components:
if var_value in c.bsa:
yield c
-
+
def find_uncovered_random_point(self, var_bounds=None, max_failings=10000):
r"""
Return a random point that satisfies the bounds and is uncovered by any cells in the complex.
Return ``None`` if the number of attemps > max_failings.
-
+
EXAMPLES::
sage: from cutgeneratingfunctionology.igp import *
@@ -2128,7 +2125,7 @@ class SemialgebraicComplex(SageObject):
The argument formal_closure whether inequalities are treated as <= 0 or as < 0.
If such point does not exist, return ``None``.
-
+
EXAMPLES::
sage: from cutgeneratingfunctionology.igp import *
@@ -2205,18 +2202,18 @@ class SemialgebraicComplex(SageObject):
self.points_to_test[num_eq] = OrderedDict()
if not num_eq in self.tested_points:
self.tested_points[num_eq] = set([])
- new_component = self._cell_class(self.family, var_value,
+ new_component = self._cell_class(self.family, var_value,
find_region_type=self.find_region_type, bddbsa=bddbsa, polynomial_map=polynomial_map)
new_num_eq = len(list(new_component.bsa.eq_poly()))
- if new_num_eq > num_eq:
- logging.warning("The cell around %s defined by %s has more equations than boundary %s" %(new_component.var_value, new_component.bsa, bddbsa))
+ if new_num_eq > num_eq:
+ logging.warning("The cell around %s defined by %s has more equations than boundary %s" % (new_component.var_value, new_component.bsa, bddbsa))
#import pdb; pdb.set_trace()
- # bsa is lower dimensional as it has more equations than bddbsa,
+ # bsa is lower dimensional as it has more equations than bddbsa,
# so we try to perturb the testpoint to obtain a
# new testpoint in bddbsa that does not fall into a lower dimensional cell.
# Heuristic code using gradient desecent. #FIXME.
- for l in (set(new_component.bsa.eq_poly())- set(bddbsa.eq_poly())):
+ for l in (set(new_component.bsa.eq_poly()) - set(bddbsa.eq_poly())):
ineqs = list(new_component.bddbsa.lt_poly())+list(new_component.bddbsa.le_poly())
pts = [find_point_flip_ineq_heuristic(var_value, l, ineqs, 1/2017), find_point_flip_ineq_heuristic(var_value, -l, ineqs, 1/2017)]
for pt in pts:
@@ -2277,7 +2274,7 @@ class SemialgebraicComplex(SageObject):
Plot the complex and store the graph.
- If restart is ``False``, plot the newly added cells on top of the last graph; otherwise, start a new graph.
- - If slice_value is given, it is either a polynomial_map that defines a section, or a list of fixed parameter values with two of them being None. Plot the section.
+ - If slice_value is given, it is either a polynomial_map that defines a section, or a list of fixed parameter values with two of them being None. Plot the section.
- plot_points controls the quality of the plotting.
EXAMPLES::
@@ -2333,7 +2330,7 @@ class SemialgebraicComplex(SageObject):
# # FIXME: zorder is broken in region_plot/ContourPlot.
# for c in self.components[self.num_plotted_components::]:
# num_eq = len(list(c.bsa.eq_poly()))
- # gc = c.plot(alpha=alpha, plot_points=plot_points, slice_value=slice_value, default_var_bound=self.default_var_bound, goto_lower_dim=goto_lower_dim, zorder=num_eq, **kwds)
+ # gc = c.plot(alpha=alpha, plot_points=plot_points, slice_value=slice_value, default_var_bound=self.default_var_bound, goto_lower_dim=goto_lower_dim, zorder=num_eq, **kwds)
# if gc: # need this because (empty g + empty gc) forgets about xmin xmax ymin ymax.
# self.graph += gc
# Workaround.
@@ -2355,11 +2352,11 @@ class SemialgebraicComplex(SageObject):
new_bsa.add_polynomial_constraint(l, operator.eq)
self.graph += new_bsa.plot(alpha=alpha, plot_points=plot_points, slice_value=slice_value, color=color, fill_color=color, xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax)
for c in components:
- if len(list(c.bsa.eq_poly()))==1:
+ if len(list(c.bsa.eq_poly())) == 1:
self.graph += c.plot(alpha=alpha, plot_points=plot_points, slice_value=slice_value, default_var_bound=self.default_var_bound, goto_lower_dim=False, zorder=0, **kwds)
if goto_lower_dim:
for c in components:
- if len(list(c.bsa.eq_poly()))==1:
+ if len(list(c.bsa.eq_poly())) == 1:
color = find_region_color(c.region_type)
for l in c.bsa.lt_poly():
new_bsa = BasicSemialgebraicSet_eq_lt_le_sets(eq=list(c.bsa.eq_poly())+[l], lt=[ll for ll in c.bsa.lt_poly() if ll != l], le=list(c.bsa.le_poly()))
@@ -2369,9 +2366,9 @@ class SemialgebraicComplex(SageObject):
new_bsa.add_polynomial_constraint(l, operator.eq)
self.graph += new_bsa.plot(alpha=alpha, plot_points=plot_points, slice_value=slice_value, color=color, fill_color=color, xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax)
for c in components:
- if len(list(c.bsa.eq_poly()))==2:
+ if len(list(c.bsa.eq_poly())) == 2:
ptcolor = find_region_color(c.region_type)
- self.graph += point(c.var_value, color = ptcolor, zorder=10)
+ self.graph += point(c.var_value, color=ptcolor, zorder=10)
self.num_plotted_components = len(self.components)
return self.graph
@@ -2408,7 +2405,7 @@ class SemialgebraicComplex(SageObject):
- var_value: the starting point. If not given, start with a random point.
- flip_ineq_step: a small positive number that controls the step length in wall-crossing.
- check_completion: When check_completion is ``True``, after bfs terminates, check whether the entire parameter space is covered by cells, using Mathematica's ``FindInstance`` (if max_failings=0) or random shooting (if max_failings>0). If an uncovered point has been found, restart the bfs from this point.
- - wall_crossing_method: 'mathematica' or 'heuristic' or 'heuristic_then_mathematica'
+ - wall_crossing_method: 'mathematica' or 'heuristic' or 'heuristic_then_mathematica'
- wall_crossing_method='heuristic_then_mathematica': try heuristic method first. If it does not find a new testpoint, then use Mathematica.
- goto_lower_dim: whether lower dimensional cells are considered. If goto_lower_dim is ``False`` or if goto_lower_dim is ``True`` and wall_crossing method is 'heuristic' but the wall is non-linear, then find new testpoint across the wall only.
@@ -2462,10 +2459,10 @@ class SemialgebraicComplex(SageObject):
uncovered_pt = self.find_uncovered_random_point(max_failings=max_failings)
if uncovered_pt is not None:
logging.warning("After bfs, the complex has uncovered point %s." % (uncovered_pt,))
- self.bfs_completion(var_value=uncovered_pt, \
- flip_ineq_step=flip_ineq_step, \
- check_completion=check_completion, \
- wall_crossing_method=wall_crossing_method, \
+ self.bfs_completion(var_value=uncovered_pt,
+ flip_ineq_step=flip_ineq_step,
+ check_completion=check_completion,
+ wall_crossing_method=wall_crossing_method,
goto_lower_dim=goto_lower_dim)
def is_complete(self, formal_closure=False):
@@ -2567,9 +2564,9 @@ def gradient(ineq):
[3*z^2 + 6*z]
"""
if hasattr(ineq, 'gradient'):
- return ineq.gradient()
+ return ineq.gradient()
else:
- return [ineq.derivative()]
+ return [ineq.derivative()]
####################################
# Find region type and region color
@@ -2702,8 +2699,8 @@ def return_result(field, result):
def result_concrete_value(field, result):
r"""
Return the concrete values in result as a tuple. See also ``result_symbolic_expression()``.
-
- This function can provided to ``find_region_type`` when setting up a :class:`SemialgebraicComplex`.
+
+ This function can provided to ``find_region_type`` when setting up a :class:`SemialgebraicComplex`.
In this way, one can compare result of type :class:`ParametricRealFieldElement` or list of :class:`ParametricRealFieldElement`
with the previous elements in ``region_type_color_map`` which do not necessarily have the same parent.
@@ -2725,8 +2722,8 @@ def result_concrete_value(field, result):
def result_symbolic_expression(field, result):
r"""
Return the symbolic expressions in result as a tuple
-
- This function can provided to ``find_region_type`` when setting up a :class:`SemialgebraicComplex`.
+
+ This function can provided to ``find_region_type`` when setting up a :class:`SemialgebraicComplex`.
In this way, one can compare result of type :class:`ParametricRealFieldElement` or list of :class:`ParametricRealFieldElement`
with the previous elements in ``region_type_color_map`` which do not necessarily have the same parent.
@@ -2750,7 +2747,7 @@ def result_symbolic_expression(field, result):
sage: vol1 == vol2
False
sage: sym_exp1 == sym_exp2
- True
+ True
"""
symbolic_expression = tuple(elt._sym if hasattr(elt, '_sym') else elt for elt in flatten([result]))
return symbolic_expression
@@ -2775,7 +2772,7 @@ def find_region_type_igp_extreme_big_cells(K, h):
h = copy(hcopy)
for x in h.values_at_end_points():
if (x < 0) or (x > 1):
- is_extreme = False
+ is_extreme = False
break
if not is_extreme:
assert (x < 0) or (x > 1)
@@ -2821,7 +2818,7 @@ def find_region_type_igp_extreme_big_cells(K, h):
ucs = generate_uncovered_components(h)
f = find_f(h)
for uncovered_pt in [f/2, (f+1)/2]:
- if any((i[0] == uncovered_pt or i[1] == uncovered_pt) for uc in ucs for i in uc if len(uc)==2):
+ if any((i[0] == uncovered_pt or i[1] == uncovered_pt) for uc in ucs for i in uc if len(uc) == 2):
uncovered_pts = [uncovered_pt]
is_extreme = False
break
@@ -2855,7 +2852,8 @@ def find_region_type_igp_extreme_big_cells(K, h):
return False
return True
-region_type_color_map = [('not_constructible', 'lightgrey'), ('is_constructible', 'black'), ('not_minimal', 'orange'), ('is_minimal', 'darkgrey'),('not_extreme', 'green'), ('is_extreme', 'blue'), ('stop', 'grey'), (True, 'blue'), (False, 'red'), ('constructible', 'darkgrey'), ('extreme', 'red')]
+
+region_type_color_map = [('not_constructible', 'lightgrey'), ('is_constructible', 'black'), ('not_minimal', 'orange'), ('is_minimal', 'darkgrey'), ('not_extreme', 'green'), ('is_extreme', 'blue'), ('stop', 'grey'), (True, 'blue'), (False, 'red'), ('constructible', 'darkgrey'), ('extreme', 'red')]
def find_region_color(region_type):
r"""
@@ -2900,11 +2898,11 @@ def color_of_ith_region_type(i):
def find_point_flip_ineq_heuristic(current_var_value, ineq, ineqs, flip_ineq_step):
r"""
- The current_var_value satisfies that l(current_var_value) <= 0 for l=ineq and for every l in ineqs,
+ The current_var_value satisfies that l(current_var_value) <= 0 for l=ineq and for every l in ineqs,
where ineq is a polynomial and ineqs is a list of polynomials.
Use heuristic method (gradient descent method with given small positive step length flip_ineq_step)
- to find a new_point (type is tuple) such that
+ to find a new_point (type is tuple) such that
ineq(new_point) > 0, l(new_point) < 0 for all l in ineqs
Return new_point, or ``None`` if it fails to find one.
@@ -3010,7 +3008,7 @@ def find_point_flip_ineq_heuristic(current_var_value, ineq, ineqs, flip_ineq_ste
ineq_value = ineq(*current_point)
try_before_fail -= 1
# print (current_point, RR(ineq_value))
- try_before_fail = max(ceil(2/flip_ineq_step), 2000) # define maximum number of walks. Considered ceil(-2 * ineq_value /flip_ineq_step) but it is too slow in the impossible cases. Added a loop with 2000 times step length when ineq_value is very negative.
+ try_before_fail = max(ceil(2/flip_ineq_step), 2000) # define maximum number of walks. Considered ceil(-2 * ineq_value /flip_ineq_step) but it is too slow in the impossible cases. Added a loop with 2000 times step length when ineq_value is very negative.
while (ineq_value <= 1e-10) and (try_before_fail > 0):
ineq_direction = vector(g(*current_point) for g in ineq_gradient)
if ineq.degree() == 1:
@@ -3034,8 +3032,8 @@ def find_point_flip_ineq_heuristic(current_var_value, ineq, ineqs, flip_ineq_ste
def adjust_pt_to_satisfy_ineqs(current_point, ineq, strict_ineqs, nonstrict_ineqs, flip_ineq_step):
r"""
- Walk from current_point (type=vector) in the direction perpendicular to
- the gradient of ineq with small positive step length flip_ineq_step,
+ Walk from current_point (type=vector) in the direction perpendicular to
+ the gradient of ineq with small positive step length flip_ineq_step,
while maintaining the value of ineq(*current_point) which is >= 0.
until get a new point such that l(new point)<0 for all l in strict_ineqs and l(new point)<=0 for all l in nonstrict_ineqs
@@ -3057,7 +3055,7 @@ def adjust_pt_to_satisfy_ineqs(current_point, ineq, strict_ineqs, nonstrict_ineq
sage: pt = adjust_pt_to_satisfy_ineqs(vector([11/8, 7/8]), a+b-2, [-a+b^2], [a-1], 1/4); pt is None
True
- Bug example in cpl Cell 9 with test point (499/1250, 488072439572/4866126017667). Without converting input to QQ, output was (0.333000000000000, 0.111333333333333) with -2*f - 3*z + 1 = -5.55111512312578e-17 , the QQ of the output=(333/1000, 167/1500) has -2*f - 3*z + 1 == 0. Revise the code to take QQ input current point::
+ Bug example in cpl Cell 9 with test point (499/1250, 488072439572/4866126017667). Without converting input to QQ, output was (0.333000000000000, 0.111333333333333) with -2*f - 3*z + 1 = -5.55111512312578e-17 , the QQ of the output=(333/1000, 167/1500) has -2*f - 3*z + 1 == 0. Revise the code to take QQ input current point::
sage: P.=QQ[]
sage: current_point = vector([0.333000000000000, 0.100300000000000]); ineq = -3*f + 1; strict_ineqs = [2*f + 2*z - 1, f + 5*z - 1, -f - 6*z + 1, -2*f - 3*z + 1]; nonstrict_ineqs = []; flip_ineq_step = 1/1000
@@ -3076,8 +3074,8 @@ def adjust_pt_to_satisfy_ineqs(current_point, ineq, strict_ineqs, nonstrict_ineq
Bug example in cpl bigcell 16 with test point (12219/26000, 24/1625). Redo with QQ had infinite loop. Bug comes from find_neighbour_point where it calls bsa_section.upstairs()._polyhedron.is_empty(), which is not strong enough. If we could test bsa_section is empty (perhaps by tighten_upstairs_by_mccormick), then this example should not appear::
- sage: P.=QQ[];
- sage: current_point = vector((71582788/143165577, 4673/377000)) # came from vector((RR(70727/150800), RR(4673/377000))),
+ sage: P.=QQ[];
+ sage: current_point = vector((71582788/143165577, 4673/377000)) # came from vector((RR(70727/150800), RR(4673/377000))),
sage: ineq=None; strict_ineqs=[2*f - 1, -9*f + 2]; nonstrict_ineqs=[4*f^2 - 4*f + 1]; flip_ineq_step=1/1000
sage: pt = adjust_pt_to_satisfy_ineqs(current_point, None, strict_ineqs, nonstrict_ineqs, flip_ineq_step=1/1000); pt is None #long time
True
@@ -3092,7 +3090,7 @@ def adjust_pt_to_satisfy_ineqs(current_point, ineq, strict_ineqs, nonstrict_ineq
#current_point is a vector
if ineq is not None:
ineq_gradient = gradient(ineq)
- if all(x.parent()==QQ for x in current_point):
+ if all(x.parent() == QQ for x in current_point):
max_walks = min(ceil(2/flip_ineq_step), 20)
else:
max_walks = min(ceil(2/flip_ineq_step), 200) #1000? # define maximum number of walks.
@@ -3154,7 +3152,7 @@ def adjust_pt_to_satisfy_ineqs(current_point, ineq, strict_ineqs, nonstrict_ineq
return None
if ineq is not None and ineq(*current_point) < 0:
return None
- if all(x.parent()==QQ for x in current_point):
+ if all(x.parent() == QQ for x in current_point):
return tuple(current_point)
else:
prec = 30 # We hope to have small denominator for the new point, so we set precision in bits = 30 is about 8 digits.
@@ -3215,13 +3213,14 @@ def embed_function_into_family(given_function, parametric_family, check_completi
var_name = []
var_value = []
for (name, value) in default_args.items():
- if not isinstance(value, bool) and not value is None:
+ if not isinstance(value, bool) and value is not None:
try:
RR(value)
var_name.append(name)
var_value.append(value)
except:
pass
+
def frt(K, h):
if h is None:
return False
@@ -3264,6 +3263,7 @@ def embed_function_into_family(given_function, parametric_family, check_completi
# plot_cpl_components(complex.components)
return {}
+
"""
EXAMPLES::
diff --git a/cutgeneratingfunctionology/spam/parametric_real_field_element.py b/cutgeneratingfunctionology/spam/parametric_real_field_element.py
index 39c6375b0..8c6e4c831 100644
--- a/cutgeneratingfunctionology/spam/parametric_real_field_element.py
+++ b/cutgeneratingfunctionology/spam/parametric_real_field_element.py
@@ -12,6 +12,7 @@
from cutgeneratingfunctionology.shared.EvaluationExceptions import FactorUndetermined
import operator
+
def richcmp_op_negation(op):
if op == op_LT:
return op_GE
@@ -28,6 +29,7 @@ def richcmp_op_negation(op):
else:
raise ValueError("{} is not a valid richcmp operator".format(op))
+
def format_richcmp_op(op):
if op == op_LT:
return '<'
@@ -44,6 +46,7 @@ def format_richcmp_op(op):
else:
raise ValueError("{} is not a valid richcmp operator".format(op))
+
class ParametricRealFieldElement(FieldElement):
r"""
A :class:`ParametricRealFieldElement` stores a symbolic expression of the parameters in the problem and a concrete value, which is the evaluation of this expression on the given parameter tuple.
@@ -85,7 +88,8 @@ def val(self):
if possible_val in possible_val.base_ring():
return possible_val
else:
- raise FactorUndetermined
+ raise FactorUndetermined.("{} cannot be evaluated because the test point is not complete".format(self.sym()))
+
def _richcmp_(left, right, op):
r"""
Examples for traditional cmp semantics::
@@ -135,7 +139,7 @@ def _richcmp_(left, right, op):
if left.parent()._big_cells:
try:
result = richcmp((left-right).val(), 0, op)
- except FactorUndetermined: # Partial evauation is happen, assume the result is True.
+ except FactorUndetermined: # Partial evaluation has happen, assume the result is True.
result = True
if result:
true_op = op
@@ -170,13 +174,8 @@ def _richcmp_(left, right, op):
left.parent().assume_comparison(right, operator.lt, left)
return richcmp(left.val(), right.val(), op)
except FactorUndetermined:
- result = True
- if result:
+ # With a partial evaluation, assume the written inequality is true.
true_op = op
- else:
- true_op = richcmp_op_negation(op)
- # left.sym() - right.sym() may cancel denominators, but that is
- # OK because _div_ makes sure that denominators are nonzero.
if true_op == op_LT:
left.parent().assume_comparison(left - right, operator.lt)
elif true_op == op_GT:
@@ -193,8 +192,6 @@ def _richcmp_(left, right, op):
raise ValueError("{} is not a valid richcmp operator".format(op))
return True
-
-
def __abs__(self):
"""
Examples for traditional cmp semantics::
@@ -436,6 +433,7 @@ def __hash__(self):
"""
return hash(self.val())
+
def is_parametric_element(x):
# We avoid using isinstance here so that this is robust even if parametric.sage is reloaded.
# For example, apparently in the test suite.
From b2aef9741527dabc9b1f5506004554cf41709787 Mon Sep 17 00:00:00 2001
From: ComboProblem <102884863+ComboProblem@users.noreply.github.com>
Date: Tue, 30 Sep 2025 10:18:16 -0700
Subject: [PATCH 12/37] spelling fixes
---
.../igp/parametric.sage | 26 +++++++++++--------
1 file changed, 15 insertions(+), 11 deletions(-)
diff --git a/cutgeneratingfunctionology/igp/parametric.sage b/cutgeneratingfunctionology/igp/parametric.sage
index 4b0734922..a8b0f9aa3 100644
--- a/cutgeneratingfunctionology/igp/parametric.sage
+++ b/cutgeneratingfunctionology/igp/parametric.sage
@@ -862,9 +862,9 @@ class ParametricRealField(Field):
else: #A numerical evaluation of the expression has failed. Assume the partial evaluation of the expression holds.
comparison_val_or_expr = self._partial_eval_factor(comparison)
if comparison_val_or_expr in base_ring:
- if not op(comparison_val_or_expr, 0): #The partial evaluation is ture, means
+ if not op(comparison_val_or_expr, 0):
raise ParametricRealFieldInconsistencyError("New constant constraint {} {} {} is not satisfied".format(lhs, op, rhs))
- else: # comparision_val_or_expr is algebraic expresion, asume the comparision here is comparionsion_val_or_expr
+ else: # comparision_val_or_expr is algebraic expression, assume the comparison here is comparionsion_val_or_expr.
comparison = comparison_val_or_expr
if comparison in base_ring:
return
@@ -1161,7 +1161,7 @@ class ParametricRealField(Field):
###############################
def find_polynomial_map(eqs=[], poly_ring=None):
"""
- BAD FUCNTION! It is used in 'mathematica' approach for non-linear case. Can we avoid it?
+ BAD FUNCTION! It is used in 'mathematica' approach for non-linear case. Can we avoid it?
Return a polynomial map that eliminates linear variables in eqs, and a dictionary recording which equations were used to eliminate those linear variables.
Assume that gaussian elimination has been performed by PPL.minimized_constraints() on the input list of equations eqs.
It is only called in SemialgebraicComplex.add_new_component in the case polynomial_map is not provided but bddbsa has equations.
@@ -1313,7 +1313,7 @@ class SemialgebraicComplexComponent(SageObject): # FIXME: Rename this to be m
sage: sorted(component.bsa.lt_poly())
[-x, 3*x - 4]
- In ProofCell region_type should alreay consider the polynomial_map::
+ In ProofCell region_type should already consider the polynomial_map::
sage: K. = ParametricRealField([1,1/2])
sage: assert(x == 2*y)
@@ -1344,10 +1344,14 @@ class SemialgebraicComplexComponent(SageObject): # FIXME: Rename this to be m
# In lower dim proof cell or non-linear equations case, some equations of K._bsa are not presented in polynomial_map.
eqs = list(K._bsa.eq_poly())
if not all(l(polynomial_map) == 0 for l in eqs):
- polynomial_map = find_polynomial_map(eqs=eqs, poly_ring=poly_ring)
+ polynomial_map = find_polynomial_map(eqs=eqs, poly_ring=poly_ring)fv
#self.bsa = K._bsa.section(polynomial_map, bsa_class='veronese', poly_ring=poly_ring) # this is a bigger_bsa
self.bsa = BasicSemialgebraicSet_veronese.from_bsa(BasicSemialgebraicSet_local(K._bsa.section(polynomial_map, poly_ring=poly_ring), self.var_value)) # TODO:, polynomial_map=list(poly_ring.gens()))
- # WHY is this input polynomial_map sometimes not compatible with the variable elimination done in bddbsa? Because upstairs ppl bsa eliminates large x_i in the inequalities, and x_i doesn't necessarily correspond to the i-th variable in poly_ring. Since polynomial_map and v_dict were not given at the initialization of veronese, the variable first encounted in the constraints is considered as x0 by upstairs ppl bsa. # In old code, we fixed the order of upstairs variables by adding initial space dimensions. We don't do that in the current code. Instead, we take the section of bddbsa to eliminate the varibles in the equations. # Is the given bddbsa required to be veronese with upstairs being ppl_bsa? Convert it anyway. # It's the same as BasicSemialgebraicSet_veronese.from_bsa(bddbsa.section(self.polynomial_map), poly_ring=poly_ring)
+ # WHY is this input polynomial_map sometimes not compatible with the variable elimination done in bddbsa?
+ # Because upstairs ppl bsa eliminates large x_i in the inequalities, and x_i doesn't necessarily correspond to the i-th variable in poly_ring.
+ # Since polynomial_map and v_dict were not given at the initialization of veronese, the variable first encountered, in the constraints is considered as x0 by upstairs ppl bsa.
+ # In old code, we fixed the order of upstairs variables by adding initial space dimensions. We don't do that in the current code. Instead, we take the section of bddbsa to eliminate the variables in the equations.
+ # Is the given bddbsa required to be veronese with upstairs being ppl_bsa? Convert it anyway. # It's the same as BasicSemialgebraicSet_veronese.from_bsa(bddbsa.section(self.polynomial_map), poly_ring=poly_ring)
self.bddbsa = BasicSemialgebraicSet_veronese.from_bsa(BasicSemialgebraicSet_local(bddbsa.section(polynomial_map, poly_ring=poly_ring), self.var_value))
# Taking section forgets the equations. Then add back the equations # Finally self.bsa should be the same as K._bsa, but its inequalities don't have variables eliminated by polynomial map, so that heuristic wall crossing can be done later.
for i in range(len(self.var_name)):
@@ -1427,13 +1431,13 @@ class SemialgebraicComplexComponent(SageObject): # FIXME: Rename this to be m
- if goto_lower_dim=False, the cell is considered as its formal closure, so no recursion into test points in lower dimensional cells.
- pos_poly is a polynomial. The return test point must satisfy pos_poly(new test point) > 0.
- OUTPUT new_points is a dictionary of dictionaries. The new_points[i] is a dictionay whose keys = candidate neighbour testpoints, values = (bddbsa whose eq_poly has i elements, polynomial_map, no_crossing_l) of the candidate neighbour cell that contains the candidate neighbour testpoint. bddbsa is recorded so that (1) complex.bddbsa is always respected; and (2) can recursively go into lower dimensional cells. polynomial_map is recorded and passed to the constructor of the neighbour cell. no_crossing is passed to the neighour cell for its find_neighbour_candidates method. We no longer update self.bsa by removing (obvious) redundant eq, lt, le constraints from its description at the end, even when 'mathematica' is used.
+ OUTPUT new_points is a dictionary of dictionaries. The new_points[i] is a dictionary whose keys = candidate neighbour testpoints, values = (bddbsa whose eq_poly has i elements, polynomial_map, no_crossing_l) of the candidate neighbour cell that contains the candidate neighbour testpoint. bddbsa is recorded so that (1) complex.bddbsa is always respected; and (2) can recursively go into lower dimensional cells. polynomial_map is recorded and passed to the constructor of the neighbour cell. no_crossing is passed to the neighbour cell for its find_neighbour_candidates method. We no longer update self.bsa by removing (obvious) redundant eq, lt, le constraints from its description at the end, even when 'mathematica' is used.
"""
bsa_eq_poly = list(self.bsa.eq_poly())
bsa_le_poly = list(self.bsa.le_poly())
bsa_lt_poly = list(self.bsa.lt_poly())
num_eq = len(bsa_eq_poly) #was len(list(self.bddbsa.eq_poly()))
- new_points = {} #dictionary with key=num_eq, value=dictionay of pt: (bddbsa, polynomial_map).
+ new_points = {} #dictionary with key=num_eq, value=dictionary of pt: (bddbsa, polynomial_map).
#bddbsa = copy(self.bddbsa)
#for l in bsa_eq_poly: # should be already in bddbsa
# bddbsa.add_polynomial_constraint(l, operator.eq)
@@ -2095,7 +2099,7 @@ class SemialgebraicComplex(SageObject):
def find_uncovered_random_point(self, var_bounds=None, max_failings=10000):
r"""
Return a random point that satisfies the bounds and is uncovered by any cells in the complex.
- Return ``None`` if the number of attemps > max_failings.
+ Return ``None`` if the number of attempts > max_failings.
EXAMPLES::
@@ -2311,7 +2315,7 @@ class SemialgebraicComplex(SageObject):
self.graph.xmin(kwds['xmin'])
xmin = kwds['xmin']
else:
- xmin = self.default_var_bound[0] # special treatement in the case goto_lower_dim which uses bsa.plot() instead of component.plot() because zorder is broken in region_plot/ContourPlot.
+ xmin = self.default_var_bound[0] # special treatment in the case goto_lower_dim which uses bsa.plot() instead of component.plot() because zorder is broken in region_plot/ContourPlot.
if 'xmax' in kwds:
self.graph.xmax(kwds['xmax'])
xmax = kwds['xmax']
@@ -2988,7 +2992,7 @@ def find_point_flip_ineq_heuristic(current_var_value, ineq, ineqs, flip_ineq_ste
sage: all(l(pt) < 0 for l in ineqs) and ineq(pt)>0
True
- Bug examle from positive definite matrix [a, b; b, 1/4], where ineq is very negative at the test point. Make big moves first, then small moves::
+ Bug example from positive definite matrix [a, b; b, 1/4], where ineq is very negative at the test point. Make big moves first, then small moves::
sage: P.=QQ[]; current_var_value = (5, 4); ineq = -4*b^2 + a; ineqs = [-a]; flip_ineq_step=1/100
sage: pt = find_point_flip_ineq_heuristic(current_var_value, ineq, ineqs, flip_ineq_step); # got pt (30943/6018, 17803/15716)
From 4f19888a678bf83477a159d61d1bd401fabadc38 Mon Sep 17 00:00:00 2001
From: ComboProblem <102884863+ComboProblem@users.noreply.github.com>
Date: Tue, 30 Sep 2025 16:21:30 -0700
Subject: [PATCH 13/37] minor fixes
---
cutgeneratingfunctionology/igp/parametric.sage | 2 +-
.../spam/parametric_real_field_element.py | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/cutgeneratingfunctionology/igp/parametric.sage b/cutgeneratingfunctionology/igp/parametric.sage
index a8b0f9aa3..5b44a94b2 100644
--- a/cutgeneratingfunctionology/igp/parametric.sage
+++ b/cutgeneratingfunctionology/igp/parametric.sage
@@ -1344,7 +1344,7 @@ class SemialgebraicComplexComponent(SageObject): # FIXME: Rename this to be m
# In lower dim proof cell or non-linear equations case, some equations of K._bsa are not presented in polynomial_map.
eqs = list(K._bsa.eq_poly())
if not all(l(polynomial_map) == 0 for l in eqs):
- polynomial_map = find_polynomial_map(eqs=eqs, poly_ring=poly_ring)fv
+ polynomial_map = find_polynomial_map(eqs=eqs, poly_ring=poly_ring)
#self.bsa = K._bsa.section(polynomial_map, bsa_class='veronese', poly_ring=poly_ring) # this is a bigger_bsa
self.bsa = BasicSemialgebraicSet_veronese.from_bsa(BasicSemialgebraicSet_local(K._bsa.section(polynomial_map, poly_ring=poly_ring), self.var_value)) # TODO:, polynomial_map=list(poly_ring.gens()))
# WHY is this input polynomial_map sometimes not compatible with the variable elimination done in bddbsa?
diff --git a/cutgeneratingfunctionology/spam/parametric_real_field_element.py b/cutgeneratingfunctionology/spam/parametric_real_field_element.py
index 8c6e4c831..a0cd5c55e 100644
--- a/cutgeneratingfunctionology/spam/parametric_real_field_element.py
+++ b/cutgeneratingfunctionology/spam/parametric_real_field_element.py
@@ -88,7 +88,7 @@ def val(self):
if possible_val in possible_val.base_ring():
return possible_val
else:
- raise FactorUndetermined.("{} cannot be evaluated because the test point is not complete".format(self.sym()))
+ raise FactorUndetermined("{} cannot be evaluated because the test point is not complete".format(self.sym()))
def _richcmp_(left, right, op):
r"""
From 10fad460b3e76a0c3b72b5341714dd398ca4a264 Mon Sep 17 00:00:00 2001
From: Matthias Koeppe
Date: Fri, 20 Dec 2024 18:30:08 -0800
Subject: [PATCH 14/37] cutgeneratingfunctionology/igp/parametric.sage: Remove
use of deprecated is_... functions
---
cutgeneratingfunctionology/igp/parametric.sage | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/cutgeneratingfunctionology/igp/parametric.sage b/cutgeneratingfunctionology/igp/parametric.sage
index 73b49bda0..908b10e9a 100644
--- a/cutgeneratingfunctionology/igp/parametric.sage
+++ b/cutgeneratingfunctionology/igp/parametric.sage
@@ -862,8 +862,8 @@ class ParametricRealField(Field):
# We use two different normalizations for the univariate and the multivariate case,
# just to match the behavior of factor() for the doctests.
# if self.ngens() == 1 can't distinguish between the two, as we know use sym_ring = PolynomialRing(QQ, names, len(names)).
- from sage.rings.polynomial.polynomial_ring import is_PolynomialRing
- if is_PolynomialRing(self._sym_field.ring()):
+ from sage.rings.polynomial.polynomial_ring import PolynomialRing_general
+ if isinstance(self._sym_field.ring(), PolynomialRing_general):
unit = abs(comparison.numerator().lc())
else:
the_lcm = lcm([coeff.denominator() for coeff in numerator.coefficients()])
From 8d71c4cb4737d8bbdec2c557813c1aaffa9195a7 Mon Sep 17 00:00:00 2001
From: Matthias Koeppe
Date: Fri, 20 Dec 2024 18:30:29 -0800
Subject: [PATCH 15/37] cutgeneratingfunctionology/spam/basic_semialgebraic.py:
Remove use of deprecated is_... functions
---
cutgeneratingfunctionology/spam/basic_semialgebraic.py | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/cutgeneratingfunctionology/spam/basic_semialgebraic.py b/cutgeneratingfunctionology/spam/basic_semialgebraic.py
index 9ba0ab697..bcd2d6823 100644
--- a/cutgeneratingfunctionology/spam/basic_semialgebraic.py
+++ b/cutgeneratingfunctionology/spam/basic_semialgebraic.py
@@ -16,8 +16,8 @@
from sage.plot.contour_plot import region_plot, implicit_plot
from sage.matrix.constructor import matrix
from sage.structure.element import get_coercion_model
-from sage.rings.polynomial.polynomial_ring import is_PolynomialRing
-from sage.rings.polynomial.multi_polynomial_ring import is_MPolynomialRing
+from sage.rings.polynomial.polynomial_ring import PolynomialRing_general
+from sage.rings.polynomial.multi_polynomial_ring_base import MPolynomialRing_base
from copy import copy
from itertools import chain
@@ -1723,7 +1723,7 @@ def __init__(self, upstairs_bsa, polynomial_map, poly_ring=None, ambient_dim=Non
if poly_ring is None:
cm = get_coercion_model()
poly_ring = cm.common_parent(*polynomial_map)
- if not is_PolynomialRing(poly_ring) and not is_MPolynomialRing(poly_ring):
+ if not isinstance(poly_ring, (PolynomialRing_general, MPolynomialRing_base)):
poly_ring = PolynomialRing(base_ring, [])
polynomial_map = [ poly_ring(f) for f in polynomial_map ]
if poly_ring and ambient_dim is None:
@@ -1903,7 +1903,7 @@ def __init__(self, upstairs_bsa=None, polynomial_map=None, v_dict=None,
if polynomial_map:
cm = get_coercion_model()
poly_ring = cm.common_parent(*polynomial_map)
- if not is_PolynomialRing(poly_ring) and not is_MPolynomialRing(poly_ring):
+ if not isinstance(poly_ring, (PolynomialRing_general, MPolynomialRing_base)):
poly_ring = PolynomialRing(base_ring, [])
polynomial_map = [ poly_ring(f) for f in polynomial_map ]
elif base_ring is not None and ambient_dim is not None:
From d697cc48ae4767315b9dccaff835d86f56c0f21d Mon Sep 17 00:00:00 2001
From: Matthias Koeppe
Date: Fri, 20 Dec 2024 18:30:54 -0800
Subject: [PATCH 16/37] cutgeneratingfunctionology/spam/real_set.py: Add #
needs
---
cutgeneratingfunctionology/spam/real_set.py | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/cutgeneratingfunctionology/spam/real_set.py b/cutgeneratingfunctionology/spam/real_set.py
index d3872e4e9..bcfddeff8 100644
--- a/cutgeneratingfunctionology/spam/real_set.py
+++ b/cutgeneratingfunctionology/spam/real_set.py
@@ -497,7 +497,7 @@ def _giac_condition_(self, variable):
EXAMPLES::
- sage: RealSet(0, 4)._giac_condition_(x)
+ sage: RealSet(0, 4)._giac_condition_(x) # needs sage.libs.giac
'((0 < sageVARx) and (sageVARx < 4))'
"""
x = variable
@@ -1441,6 +1441,7 @@ def _giac_condition_(self, variable):
EXAMPLES::
+ sage: # needs sage.libs.giac
sage: from cutgeneratingfunctionology.spam.real_set import RealSet
sage: RealSet(0, 1)._giac_condition_(x)
'((0 < sageVARx) and (sageVARx < 1))'
@@ -1453,9 +1454,9 @@ def _giac_condition_(self, variable):
TESTS::
- sage: RealSet(6,6)._giac_condition_(x)
+ sage: RealSet(6,6)._giac_condition_(x) # needs sage.libs.giac
'false'
- sage: RealSet([6,6])._giac_condition_(x)
+ sage: RealSet([6,6])._giac_condition_(x) # needs sage.libs.giac
'sageVARx == 6'
"""
x = variable
From 9d0816ffbf0b06a90fa8d7f9fa1ee34cd12756c1 Mon Sep 17 00:00:00 2001
From: Matthias Koeppe
Date: Fri, 20 Dec 2024 18:31:11 -0800
Subject: [PATCH 17/37]
cutgeneratingfunctionology/spam/basic_semialgebraic_linear_system.py: Add #
needs
---
.../spam/basic_semialgebraic_linear_system.py | 3 +++
1 file changed, 3 insertions(+)
diff --git a/cutgeneratingfunctionology/spam/basic_semialgebraic_linear_system.py b/cutgeneratingfunctionology/spam/basic_semialgebraic_linear_system.py
index f4442dcbb..10f44c28a 100644
--- a/cutgeneratingfunctionology/spam/basic_semialgebraic_linear_system.py
+++ b/cutgeneratingfunctionology/spam/basic_semialgebraic_linear_system.py
@@ -54,6 +54,8 @@ def __init__(self, base_ring=None, ambient_dim=None, poly_ring=None, eq=[], lt=[
x - y + ((U*W + b)~)*z + (-U*W)~}
A non-parametric example::
+
+ sage: # needs sage.groups
sage: D = polytopes.dodecahedron()
sage: D.ambient_dim()
3
@@ -68,6 +70,7 @@ def __init__(self, base_ring=None, ambient_dim=None, poly_ring=None, eq=[], lt=[
True
Another non-parametric example::
+
sage: b3 = polytopes.Birkhoff_polytope(3)
sage: b3.ambient_dim()
9
From 034b336d7bd4a518bdcfcdc146dd96024df1c6d3 Mon Sep 17 00:00:00 2001
From: Matthias Koeppe
Date: Sun, 22 Dec 2024 22:06:47 -0800
Subject: [PATCH 18/37] tox.ini (passagemath): Fix up
---
tox.ini | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/tox.ini b/tox.ini
index 38ef6376e..50736b622 100644
--- a/tox.ini
+++ b/tox.ini
@@ -12,10 +12,12 @@ passenv =
HOME
GITHUB_ACTIONS
-# We test with the top level sage.all__sagemath_modules (not ...polyhedra)
-# to enforce use of 'from sage_numerical_interactive_mip import *' in all doctests.
+setenv =
+ # For access to _doctest_environment.py
+ PYTHONPATH=.
+
commands =
- bash -c "make check SAGE_CHECK_FLAGS=--environment=sage.all__sagemath_polyhedra"
+ bash -c "make check SAGE_CHECK_FLAGS=--environment=_doctest_environment"
[testenv:docs]
allowlist_externals =
From c85d95fb7f952af983723baf716c65dd54792763 Mon Sep 17 00:00:00 2001
From: Matthias Koeppe
Date: Sun, 22 Dec 2024 22:07:11 -0800
Subject: [PATCH 19/37] pyproject.toml (passagemath extra): Unpin
---
pyproject.toml | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)
diff --git a/pyproject.toml b/pyproject.toml
index 53f289010..80cef0485 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -35,16 +35,20 @@ dynamic = ['version']
[project.optional-dependencies]
doc = [
+ "cutgeneratingfunctionology[passagemath]",
"sage-package",
"sphinx",
"sphinxcontrib-bibtex",
]
passagemath = [
- "passagemath-polyhedra==10.4.40",
- "passagemath-plot==10.4.40",
- "passagemath-repl==10.4.40",
- "passagemath-flint==10.4.40",
- "passagemath-graphs==10.4.40",
+ "passagemath-pari",
+ "passagemath-polyhedra",
+ "passagemath-plot",
+ "passagemath-repl",
+ "passagemath-flint",
+ "passagemath-graphs",
+ "passagemath-singular",
+ "passagemath-symbolics",
]
[project.readme]
From 99f267dd1f69e478b33c0cdd8a0d567c1f6da34b Mon Sep 17 00:00:00 2001
From: Matthias Koeppe
Date: Sun, 22 Dec 2024 22:07:45 -0800
Subject: [PATCH 20/37] _doctest_environment.py: New
---
_doctest_environment.py | 5 +++++
1 file changed, 5 insertions(+)
create mode 100644 _doctest_environment.py
diff --git a/_doctest_environment.py b/_doctest_environment.py
new file mode 100644
index 000000000..4c2ebe383
--- /dev/null
+++ b/_doctest_environment.py
@@ -0,0 +1,5 @@
+# Toplevel for doctesting with passagemath
+
+from sage.all__sagemath_flint import *
+from sage.all__sagemath_polyhedra import *
+from sage.all__sagemath_symbolics import *
From 36d1ae11e61bc6d59607d5e023e7d6f948f1cde9 Mon Sep 17 00:00:00 2001
From: Matthias Koeppe
Date: Sun, 22 Dec 2024 22:09:21 -0800
Subject: [PATCH 21/37] .gitignore: Add more
---
.gitignore | 2 ++
1 file changed, 2 insertions(+)
diff --git a/.gitignore b/.gitignore
index 58b9d7c83..b614431ce 100644
--- a/.gitignore
+++ b/.gitignore
@@ -41,3 +41,5 @@ region_graphics/*
*conflicted copy*
/survey_graphics/*/*.png
/Makefile.conf
+
+*.egg-info
From d847df29a5c30a4990d8e7b9695091a30b666d4a Mon Sep 17 00:00:00 2001
From: Matthias Koeppe
Date: Sun, 22 Dec 2024 23:08:32 -0800
Subject: [PATCH 22/37] README.rst: Document passagemath install method
---
README.rst | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/README.rst b/README.rst
index 7ba88f9b0..eadf1ae4d 100644
--- a/README.rst
+++ b/README.rst
@@ -175,3 +175,33 @@ D. Run in a standalone installation of the SageMath distribution (no conda)
import cutgeneratingfunctionology.igp as igp; from cutgeneratingfunctionology.igp import *
- Follow the instructions and examples in https://github.com/mkoeppe/cutgeneratingfunctionology/blob/master/demo.rst or https://github.com/mkoeppe/cutgeneratingfunctionology/blob/master/demo.ipynb .
+
+
+E. Run in Python (no installation of SageMath required)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This method uses the modularized distributions from https://github.com/passagemath
+
+- Clone the GitHub repository https://github.com/mkoeppe/cutgeneratingfunctionology.git::
+
+ git clone https://github.com/mkoeppe/cutgeneratingfunctionology.git
+ cd cutgeneratingfunctionology
+
+- (Optional:) Create a virtual environment
+
+ python3 -m venv venv-cgf
+ . venv-cgf/bin/activate
+
+- Install the cutgeneratingfunctionology package using pip::
+
+ pip install ".[passagemath]"
+
+- Start Python::
+
+ python3
+
+- At the Python prompt, type::
+
+ import cutgeneratingfunctionology.igp as igp; from cutgeneratingfunctionology.igp import *
+
+- Follow the instructions and examples in https://github.com/mkoeppe/cutgeneratingfunctionology/blob/master/demo.rst or https://github.com/mkoeppe/cutgeneratingfunctionology/blob/master/demo.ipynb .
From 482d4c444aaee85e728ab63db3da8a0ba87b2bed Mon Sep 17 00:00:00 2001
From: ComboProblem <102884863+ComboProblem@users.noreply.github.com>
Date: Thu, 21 Aug 2025 18:12:20 -0700
Subject: [PATCH 23/37] add _partial_eval method to ParametricRealField
---
.../igp/parametric.sage | 22 +++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/cutgeneratingfunctionology/igp/parametric.sage b/cutgeneratingfunctionology/igp/parametric.sage
index 908b10e9a..b5bd9da43 100644
--- a/cutgeneratingfunctionology/igp/parametric.sage
+++ b/cutgeneratingfunctionology/igp/parametric.sage
@@ -562,6 +562,28 @@ class ParametricRealField(Field):
except TypeError: # 'None' components
pass
raise FactorUndetermined("{} cannot be evaluated because the test point is not complete".format(fac))
+
+ def _partial_eval_factor(self, fac):
+ """
+ Partially evaluate ``fac`` on the test point.
+
+ This function is only intended to be called after ``FactorUndetermined`` is raised from ``_eval_factor``.
+ """
+ val_dict = {sym:val for sym, val zip([symb.sym() for symb in self._gens], self._values) if val is not None}
+ return fac.subs(val_dict)
+
+# Returns a symbolic expression or raises an ``EvaluationSuccessfulFlag``.
+# Receiving an ``EvaluationSuccessfulFlag`` means ``fac`` can be evaluated with the known values of the
+# test point.
+# base_ring = self._sym_field.base_ring()
+# if fac in base_ring:
+# raise EvaluationSuccessfulFlag("{} can be evaluated in the base_ring. Use _eval_factor instead.".format(fac))
+# try:
+# fac(self._values)
+# raise EvaluationSuccessfulFlag("{} can be evaluated with the test point. Use _eval_factor instead.".format(fac))
+# except TypeError:
+# val_dict = {sym:val for sym, val zip([symb.sym() for symb in self._gens], self._values) if val is not None}
+# return fac.subs(val_dict)
def _factor_sign(self, fac):
"""
From 3a069d28bf2d36b459d4af8abe91959bc3569752 Mon Sep 17 00:00:00 2001
From: ComboProblem <102884863+ComboProblem@users.noreply.github.com>
Date: Thu, 21 Aug 2025 18:13:22 -0700
Subject: [PATCH 24/37] update doc to design goals for partial test points
---
cutgeneratingfunctionology/igp/parametric.sage | 17 +++++++----------
1 file changed, 7 insertions(+), 10 deletions(-)
diff --git a/cutgeneratingfunctionology/igp/parametric.sage b/cutgeneratingfunctionology/igp/parametric.sage
index b5bd9da43..04f688b69 100644
--- a/cutgeneratingfunctionology/igp/parametric.sage
+++ b/cutgeneratingfunctionology/igp/parametric.sage
@@ -158,25 +158,22 @@ class ParametricRealField(Field):
sage: f[0]*f[1] <= 4
True
- Test-point free mode (limited functionality and MUCH slower because of many more polynoial
- evaluations via libsingular)::
+ Test-point free descriptions can be written which every comparison is assumed to be true.
+ MUCH slower because of many more polynoial evaluations via libsingular::
sage: K. = ParametricRealField(None, mutable_values=True)
sage: a <= 2
- Traceback (most recent call last):
- ...
- FactorUndetermined: a cannot be evaluated because the test point is not complete
+ True
sage: K.assume_comparison(a.sym(), operator.le, 3)
- Partial test point mode::
+ Comparisons with test-points that are partially defined are supported. Comparisons made in
+ unspecified variables are assumed to be true::
sage: K. = ParametricRealField([None, 1], mutable_values=True)
sage: a <= 2
- Traceback (most recent call last):
- ...
- FactorUndetermined: a cannot be evaluated because the test point is not complete
- sage: b <= 11
True
+ sage: b <= 11
+ True
"""
Element = ParametricRealFieldElement
From d587a85d6152bced09f63ff60aaaeed9ce58a4a0 Mon Sep 17 00:00:00 2001
From: ComboProblem <102884863+ComboProblem@users.noreply.github.com>
Date: Thu, 21 Aug 2025 18:18:16 -0700
Subject: [PATCH 25/37] change assume_comparison to support partial evaluations
---
cutgeneratingfunctionology/igp/parametric.sage | 15 ++++++++++++---
1 file changed, 12 insertions(+), 3 deletions(-)
diff --git a/cutgeneratingfunctionology/igp/parametric.sage b/cutgeneratingfunctionology/igp/parametric.sage
index 04f688b69..7628e7541 100644
--- a/cutgeneratingfunctionology/igp/parametric.sage
+++ b/cutgeneratingfunctionology/igp/parametric.sage
@@ -174,7 +174,6 @@ class ParametricRealField(Field):
True
sage: b <= 11
True
-
"""
Element = ParametricRealFieldElement
@@ -859,14 +858,24 @@ class ParametricRealField(Field):
try:
comparison_val = comparison.val()
except FactorUndetermined:
- comparison_val = None
+ # partial test point evaluation; assume evaluation is true
+ # so record the assumed factor in the BSA
+ # it is the responsibility of the BSA to know if it is empty or not
+ # most implementations of BSAs cannot do this for non-linear cases.
+ assumed_fac = self._partial_eval_factor(comparison)
+ if not is_factor_known(assumed_fac):
+ record_comparision(assumed_fac, op)
+ return
comparison = comparison.sym()
else:
comparison = self._sym_field(comparison)
try:
comparison_val = self._eval_factor(comparison)
except FactorUndetermined:
- comparison_val = None
+ assumed_fac = self._partial_eval_factor(comparison)
+ if not is_factor_known(assumed_fac):
+ record_comparision(assumed_fac, op)
+ return
if comparison_val is not None:
if not op(comparison_val, 0):
if comparison in base_ring:
From 6ef0a19d8c40fd1801c79ab51aeaf5449ee14a65 Mon Sep 17 00:00:00 2001
From: ComboProblem <102884863+ComboProblem@users.noreply.github.com>
Date: Fri, 22 Aug 2025 14:34:21 -0700
Subject: [PATCH 26/37] fix syntax
---
cutgeneratingfunctionology/igp/parametric.sage | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/cutgeneratingfunctionology/igp/parametric.sage b/cutgeneratingfunctionology/igp/parametric.sage
index 7628e7541..78d96b260 100644
--- a/cutgeneratingfunctionology/igp/parametric.sage
+++ b/cutgeneratingfunctionology/igp/parametric.sage
@@ -565,7 +565,9 @@ class ParametricRealField(Field):
This function is only intended to be called after ``FactorUndetermined`` is raised from ``_eval_factor``.
"""
- val_dict = {sym:val for sym, val zip([symb.sym() for symb in self._gens], self._values) if val is not None}
+ syms = [symb.sym() for symb in self._gens]
+ val_dict = {sym:val for sym, val in zip(syms , self._values) if val is not None}
+ print(val_dict)
return fac.subs(val_dict)
# Returns a symbolic expression or raises an ``EvaluationSuccessfulFlag``.
From 60e52ccf05dea6e44d316019977f9dfce1571ab1 Mon Sep 17 00:00:00 2001
From: ComboProblem <102884863+ComboProblem@users.noreply.github.com>
Date: Fri, 22 Aug 2025 14:52:21 -0700
Subject: [PATCH 27/37] move FactorUndetermined to its own file to make it a
shared resouce
---
cutgeneratingfunctionology/igp/parametric.sage | 4 +---
cutgeneratingfunctionology/shared/EvaluationExceptions.py | 2 ++
.../spam/parametric_real_field_element.py | 3 ++-
3 files changed, 5 insertions(+), 4 deletions(-)
create mode 100644 cutgeneratingfunctionology/shared/EvaluationExceptions.py
diff --git a/cutgeneratingfunctionology/igp/parametric.sage b/cutgeneratingfunctionology/igp/parametric.sage
index 78d96b260..8facd9031 100644
--- a/cutgeneratingfunctionology/igp/parametric.sage
+++ b/cutgeneratingfunctionology/igp/parametric.sage
@@ -20,6 +20,7 @@ from cutgeneratingfunctionology.spam.basic_semialgebraic_local import BasicSemia
from cutgeneratingfunctionology.spam.semialgebraic_mathematica import BasicSemialgebraicSet_mathematica, from_mathematica
from cutgeneratingfunctionology.spam.basic_semialgebraic_groebner_basis import BasicSemialgebraicSet_groebner_basis
from cutgeneratingfunctionology.spam.polyhedral_complex import PolyhedralComplex
+from cutgeneratingfunctionology.shared.EvaluationExceptions import FactorUndetermined
from .parametric_family import Classcall, ParametricFamily_base, ParametricFamily
debug_new_factors = False
@@ -58,9 +59,6 @@ class ParametricRealFieldRefinementError(ValueError):
from contextlib import contextmanager
-class FactorUndetermined(Exception):
- pass
-
allow_refinement_default = True
big_cells_default = 'if_not_allow_refinement'
mutable_values_default = False
diff --git a/cutgeneratingfunctionology/shared/EvaluationExceptions.py b/cutgeneratingfunctionology/shared/EvaluationExceptions.py
new file mode 100644
index 000000000..7e7dd6e04
--- /dev/null
+++ b/cutgeneratingfunctionology/shared/EvaluationExceptions.py
@@ -0,0 +1,2 @@
+class FactorUndetermined(Exception): # FactorUndetermined is raised when an expression can not be evaluated with a test point.
+ pass
\ No newline at end of file
diff --git a/cutgeneratingfunctionology/spam/parametric_real_field_element.py b/cutgeneratingfunctionology/spam/parametric_real_field_element.py
index c6688eeeb..40c1a4ea9 100644
--- a/cutgeneratingfunctionology/spam/parametric_real_field_element.py
+++ b/cutgeneratingfunctionology/spam/parametric_real_field_element.py
@@ -9,6 +9,7 @@
from sage.rings.real_mpfr import RR
from sage.functions.other import ceil, floor
from sage.functions.generalized import sign
+from cutgeneratingfunctionology.shared.EvaluationExceptions import FactorUndetermined
import operator
def richcmp_op_negation(op):
@@ -20,7 +21,7 @@ def richcmp_op_negation(op):
return op_NE
elif op == op_NE:
return op_EQ
- elif op == op_GT:
+ elif op == op_GT:s
return op_LE
elif op == op_GE:
return op_LT
From 53565701157ecf4e6b4515965e1d270605ff674f Mon Sep 17 00:00:00 2001
From: ComboProblem <102884863+ComboProblem@users.noreply.github.com>
Date: Thu, 28 Aug 2025 15:13:15 -0700
Subject: [PATCH 28/37] inprogress
---
.../igp/parametric.sage | 30 +++++++++++--------
.../spam/parametric_real_field_element.py | 17 +++++++----
2 files changed, 30 insertions(+), 17 deletions(-)
diff --git a/cutgeneratingfunctionology/igp/parametric.sage b/cutgeneratingfunctionology/igp/parametric.sage
index 8facd9031..4dbc7b660 100644
--- a/cutgeneratingfunctionology/igp/parametric.sage
+++ b/cutgeneratingfunctionology/igp/parametric.sage
@@ -563,9 +563,7 @@ class ParametricRealField(Field):
This function is only intended to be called after ``FactorUndetermined`` is raised from ``_eval_factor``.
"""
- syms = [symb.sym() for symb in self._gens]
- val_dict = {sym:val for sym, val in zip(syms , self._values) if val is not None}
- print(val_dict)
+ val_dict = {sym:val for sym, val in zip(fac.parent().gens() , self._values) if val is not None}
return fac.subs(val_dict)
# Returns a symbolic expression or raises an ``EvaluationSuccessfulFlag``.
@@ -858,14 +856,21 @@ class ParametricRealField(Field):
try:
comparison_val = comparison.val()
except FactorUndetermined:
- # partial test point evaluation; assume evaluation is true
- # so record the assumed factor in the BSA
- # it is the responsibility of the BSA to know if it is empty or not
- # most implementations of BSAs cannot do this for non-linear cases.
+ # Partial test point evaluation assumes the partially
+ # evaluated factor is True.
+ # So, we record the assumed factor in the BSA without checking if the factor
+ # should be addeded or not.
+ # It becomes the responsibility of the BSA to detemined if the recorded factors
+ # so far repersent a non-empty BSA.
+ # Most implementations of BSAs cannot do this for non-linear cases.
+ # With a BSA that is equipped with a first order logic solver like QPEAD
+ # should be able to do this.
assumed_fac = self._partial_eval_factor(comparison)
- if not is_factor_known(assumed_fac):
- record_comparision(assumed_fac, op)
- return
+ self.record_factor(assumed_fac, op)
+ print(assumed_fac)
+ if self._bsa.is_empty():
+ raise ParametricRealFieldInconsistencyError("Assumed constraint {} derivied from the comparision {} {} {} is inconsistent with already recoreded constraints".format(assumed_fac, lhs, op, rhs))
+ return
comparison = comparison.sym()
else:
comparison = self._sym_field(comparison)
@@ -873,8 +878,9 @@ class ParametricRealField(Field):
comparison_val = self._eval_factor(comparison)
except FactorUndetermined:
assumed_fac = self._partial_eval_factor(comparison)
- if not is_factor_known(assumed_fac):
- record_comparision(assumed_fac, op)
+ if not self.is_factor_known(assumed_fac, op):
+ self.record_factor(assumed_fac, op)
+ print("here", assumed_fac)
return
if comparison_val is not None:
if not op(comparison_val, 0):
diff --git a/cutgeneratingfunctionology/spam/parametric_real_field_element.py b/cutgeneratingfunctionology/spam/parametric_real_field_element.py
index 40c1a4ea9..99d6a82fc 100644
--- a/cutgeneratingfunctionology/spam/parametric_real_field_element.py
+++ b/cutgeneratingfunctionology/spam/parametric_real_field_element.py
@@ -21,7 +21,7 @@ def richcmp_op_negation(op):
return op_NE
elif op == op_NE:
return op_EQ
- elif op == op_GT:s
+ elif op == op_GT:
return op_LE
elif op == op_GE:
return op_LT
@@ -77,9 +77,13 @@ def sym(self):
def val(self):
try:
return self._val
- except AttributeError:
- return self.parent()._eval_factor(self._sym)
-
+ except AttributeError: # with imutable values, this fales because we get some hash map weirdness
+ try:
+ return self.parent()._eval_factor(self._sym)
+ except FactorUndetermined:
+ possible_val = self.parent()._partial_eval_factor(self._sym)
+ if possible_val in possible_val.base_ring():
+ return possible_val
def _richcmp_(left, right, op):
r"""
Examples for traditional cmp semantics::
@@ -127,7 +131,10 @@ def _richcmp_(left, right, op):
# shouldn't really happen, within coercion
raise TypeError("comparing elements from different fields")
if left.parent()._big_cells:
- result = richcmp(left.val(), right.val(), op)
+ try:
+ result = richcmp(left.val(), right.val(), op)
+ except FactorUndetermined:
+ result = True
if result:
true_op = op
else:
From 17fbbe5a22437f517ff870dc9498df60b55545de Mon Sep 17 00:00:00 2001
From: ComboProblem <102884863+ComboProblem@users.noreply.github.com>
Date: Sat, 30 Aug 2025 10:39:31 -0700
Subject: [PATCH 29/37] working partial test point more, next step fix doc
string tests
---
.../igp/parametric.sage | 31 ++++++-------------
1 file changed, 10 insertions(+), 21 deletions(-)
diff --git a/cutgeneratingfunctionology/igp/parametric.sage b/cutgeneratingfunctionology/igp/parametric.sage
index 4dbc7b660..2ae87bb7f 100644
--- a/cutgeneratingfunctionology/igp/parametric.sage
+++ b/cutgeneratingfunctionology/igp/parametric.sage
@@ -171,7 +171,7 @@ class ParametricRealField(Field):
sage: a <= 2
True
sage: b <= 11
- True
+ True
"""
Element = ParametricRealFieldElement
@@ -856,38 +856,27 @@ class ParametricRealField(Field):
try:
comparison_val = comparison.val()
except FactorUndetermined:
- # Partial test point evaluation assumes the partially
- # evaluated factor is True.
- # So, we record the assumed factor in the BSA without checking if the factor
- # should be addeded or not.
- # It becomes the responsibility of the BSA to detemined if the recorded factors
- # so far repersent a non-empty BSA.
- # Most implementations of BSAs cannot do this for non-linear cases.
- # With a BSA that is equipped with a first order logic solver like QPEAD
- # should be able to do this.
- assumed_fac = self._partial_eval_factor(comparison)
- self.record_factor(assumed_fac, op)
- print(assumed_fac)
- if self._bsa.is_empty():
- raise ParametricRealFieldInconsistencyError("Assumed constraint {} derivied from the comparision {} {} {} is inconsistent with already recoreded constraints".format(assumed_fac, lhs, op, rhs))
- return
+ comparison_val = None
comparison = comparison.sym()
else:
comparison = self._sym_field(comparison)
try:
comparison_val = self._eval_factor(comparison)
except FactorUndetermined:
- assumed_fac = self._partial_eval_factor(comparison)
- if not self.is_factor_known(assumed_fac, op):
- self.record_factor(assumed_fac, op)
- print("here", assumed_fac)
- return
+ comparison_val = None
if comparison_val is not None:
if not op(comparison_val, 0):
if comparison in base_ring:
raise ParametricRealFieldInconsistencyError("New constant constraint {} {} {} is not satisfied".format(lhs, op, rhs))
else:
raise ParametricRealFieldInconsistencyError("New constraint {} {} {} is not satisfied by the test point".format(lhs, op, rhs))
+ else: #A numerical evaluation of the expression has failed. Assume the partial evaluation of the expression holds.
+ comparison_val_or_expr = self._partial_eval_factor(comparison)
+ if not op(comparison_val_or_expr, 0): #The partial evual
+ if comparison_val_or_expr in base_ring:
+ raise ParametricRealFieldInconsistencyError("New constant constraint {} {} {} is not satisfied".format(lhs, op, rhs))
+ else: # comparision_val_or_expr is algebraic expresion, the only "true" comparision here is comparionsion val_or_expr
+ comparison = comparison_val_or_expr
if comparison in base_ring:
return
if comparison.denominator() == 1 and comparison.numerator().degree() == 1:
From 9d4bee46f4dc11c2e6da81e80f995a0d6d193b03 Mon Sep 17 00:00:00 2001
From: ComboProblem <102884863+ComboProblem@users.noreply.github.com>
Date: Sat, 30 Aug 2025 15:12:41 -0700
Subject: [PATCH 30/37] finish comments; clean up tests.
---
cutgeneratingfunctionology/igp/parametric.sage | 17 ++++++++---------
.../spam/parametric_real_field_element.py | 2 +-
2 files changed, 9 insertions(+), 10 deletions(-)
diff --git a/cutgeneratingfunctionology/igp/parametric.sage b/cutgeneratingfunctionology/igp/parametric.sage
index 2ae87bb7f..631226716 100644
--- a/cutgeneratingfunctionology/igp/parametric.sage
+++ b/cutgeneratingfunctionology/igp/parametric.sage
@@ -162,7 +162,8 @@ class ParametricRealField(Field):
sage: K. = ParametricRealField(None, mutable_values=True)
sage: a <= 2
True
- sage: K.assume_comparison(a.sym(), operator.le, 3)
+ sage: K. = ParametricRealField(None, mutable_values=True)
+ sage: K.assume_comparison(a.sym(), operator.le, 2)
Comparisons with test-points that are partially defined are supported. Comparisons made in
unspecified variables are assumed to be true::
@@ -312,7 +313,7 @@ class ParametricRealField(Field):
sage: sqrt2, = nice_field_values([sqrt(2)])
sage: K. = ParametricRealField([0], base_ring=sqrt2.parent())
sage: f + sqrt2
- (f + (a))~
+ (f + a)~
This currently does not work for Sage's built-in embedded number field elements...
"""
@@ -404,9 +405,7 @@ class ParametricRealField(Field):
....: with K.temporary_assumptions():
....: K.assume_comparison(a.sym(), operator.le, 3)
....: a <= 4
- Traceback (most recent call last):
- ...
- FactorUndetermined: a cannot be evaluated because the test point is not complete...
+ True
"""
self._values = [ None for n in self._names ]
@@ -872,11 +871,11 @@ class ParametricRealField(Field):
raise ParametricRealFieldInconsistencyError("New constraint {} {} {} is not satisfied by the test point".format(lhs, op, rhs))
else: #A numerical evaluation of the expression has failed. Assume the partial evaluation of the expression holds.
comparison_val_or_expr = self._partial_eval_factor(comparison)
- if not op(comparison_val_or_expr, 0): #The partial evual
- if comparison_val_or_expr in base_ring:
+ if comparison_val_or_expr in base_ring:
+ if not op(comparison_val_or_expr, 0): #The partial evaluation is ture, means
raise ParametricRealFieldInconsistencyError("New constant constraint {} {} {} is not satisfied".format(lhs, op, rhs))
- else: # comparision_val_or_expr is algebraic expresion, the only "true" comparision here is comparionsion val_or_expr
- comparison = comparison_val_or_expr
+ else: # comparision_val_or_expr is algebraic expresion, asume the comparision here is comparionsion_val_or_expr
+ comparison = comparison_val_or_expr
if comparison in base_ring:
return
if comparison.denominator() == 1 and comparison.numerator().degree() == 1:
diff --git a/cutgeneratingfunctionology/spam/parametric_real_field_element.py b/cutgeneratingfunctionology/spam/parametric_real_field_element.py
index 99d6a82fc..656734c4a 100644
--- a/cutgeneratingfunctionology/spam/parametric_real_field_element.py
+++ b/cutgeneratingfunctionology/spam/parametric_real_field_element.py
@@ -133,7 +133,7 @@ def _richcmp_(left, right, op):
if left.parent()._big_cells:
try:
result = richcmp(left.val(), right.val(), op)
- except FactorUndetermined:
+ except FactorUndetermined: # Partial evauation is happen, assume the result is True.
result = True
if result:
true_op = op
From 6e65420048d7b0e5ba25f24a6b80f6d1c97f93c0 Mon Sep 17 00:00:00 2001
From: ComboProblem <102884863+ComboProblem@users.noreply.github.com>
Date: Sat, 30 Aug 2025 15:19:42 -0700
Subject: [PATCH 31/37] remove some comments
---
.../spam/parametric_real_field_element.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cutgeneratingfunctionology/spam/parametric_real_field_element.py b/cutgeneratingfunctionology/spam/parametric_real_field_element.py
index 656734c4a..b50c7d8d1 100644
--- a/cutgeneratingfunctionology/spam/parametric_real_field_element.py
+++ b/cutgeneratingfunctionology/spam/parametric_real_field_element.py
@@ -77,7 +77,7 @@ def sym(self):
def val(self):
try:
return self._val
- except AttributeError: # with imutable values, this fales because we get some hash map weirdness
+ except AttributeError:
try:
return self.parent()._eval_factor(self._sym)
except FactorUndetermined:
From d34a17c2a05a2c54899243ef63243e22999aabce Mon Sep 17 00:00:00 2001
From: ComboProblem <102884863+ComboProblem@users.noreply.github.com>
Date: Mon, 29 Sep 2025 18:58:27 -0700
Subject: [PATCH 32/37] finish fixing tests
---
.../spam/parametric_real_field_element.py | 43 ++++++++++++++++---
1 file changed, 36 insertions(+), 7 deletions(-)
diff --git a/cutgeneratingfunctionology/spam/parametric_real_field_element.py b/cutgeneratingfunctionology/spam/parametric_real_field_element.py
index b50c7d8d1..39c6375b0 100644
--- a/cutgeneratingfunctionology/spam/parametric_real_field_element.py
+++ b/cutgeneratingfunctionology/spam/parametric_real_field_element.py
@@ -84,6 +84,8 @@ def val(self):
possible_val = self.parent()._partial_eval_factor(self._sym)
if possible_val in possible_val.base_ring():
return possible_val
+ else:
+ raise FactorUndetermined
def _richcmp_(left, right, op):
r"""
Examples for traditional cmp semantics::
@@ -132,7 +134,7 @@ def _richcmp_(left, right, op):
raise TypeError("comparing elements from different fields")
if left.parent()._big_cells:
try:
- result = richcmp(left.val(), right.val(), op)
+ result = richcmp((left-right).val(), 0, op)
except FactorUndetermined: # Partial evauation is happen, assume the result is True.
result = True
if result:
@@ -158,13 +160,40 @@ def _richcmp_(left, right, op):
return result
else:
# Traditional cmp semantics.
- if (left.val() == right.val()):
- left.parent().assume_comparison(right, operator.eq, left)
- elif (left.val() < right.val()):
- left.parent().assume_comparison(left, operator.lt, right)
+ try:
+ expr_val = (left-right).val()
+ if( expr_val == 0):
+ left.parent().assume_comparison(right, operator.eq, left)
+ elif (expr_val < 0):
+ left.parent().assume_comparison(left, operator.lt, right)
+ else:
+ left.parent().assume_comparison(right, operator.lt, left)
+ return richcmp(left.val(), right.val(), op)
+ except FactorUndetermined:
+ result = True
+ if result:
+ true_op = op
+ else:
+ true_op = richcmp_op_negation(op)
+ # left.sym() - right.sym() may cancel denominators, but that is
+ # OK because _div_ makes sure that denominators are nonzero.
+ if true_op == op_LT:
+ left.parent().assume_comparison(left - right, operator.lt)
+ elif true_op == op_GT:
+ left.parent().assume_comparison(left - right, operator.gt)
+ elif true_op == op_EQ:
+ left.parent().assume_comparison(right - left, operator.eq)
+ elif true_op == op_LE:
+ left.parent().assume_comparison(left - right, operator.le)
+ elif true_op == op_GE:
+ left.parent().assume_comparison(left - right, operator.ge)
+ elif true_op == op_NE:
+ left.parent().assume_comparison(right - left, operator.ne)
else:
- left.parent().assume_comparison(right, operator.lt, left)
- return richcmp(left.val(), right.val(), op)
+ raise ValueError("{} is not a valid richcmp operator".format(op))
+ return True
+
+
def __abs__(self):
"""
From 2c70459d64c931ebe8445cc131cd9857cdb1b2af Mon Sep 17 00:00:00 2001
From: ComboProblem <102884863+ComboProblem@users.noreply.github.com>
Date: Tue, 30 Sep 2025 10:07:00 -0700
Subject: [PATCH 33/37] linting
---
.../igp/parametric.sage | 146 +++++++++---------
.../spam/parametric_real_field_element.py | 18 +--
2 files changed, 81 insertions(+), 83 deletions(-)
diff --git a/cutgeneratingfunctionology/igp/parametric.sage b/cutgeneratingfunctionology/igp/parametric.sage
index 631226716..4b0734922 100644
--- a/cutgeneratingfunctionology/igp/parametric.sage
+++ b/cutgeneratingfunctionology/igp/parametric.sage
@@ -37,12 +37,12 @@ def bigcellify_igp():
import cutgeneratingfunctionology.spam.big_cells as big_cells
big_cells.bigcellify_module(igp)
+
###############################
# Parametric Real Number Field
###############################
from cutgeneratingfunctionology.spam.parametric_real_field_element import ParametricRealFieldElement, is_parametric_element
-
from sage.rings.ring import Field
import sage.rings.number_field.number_field_base as number_field_base
from sage.structure.coerce_maps import CallableConvertMap
@@ -51,12 +51,15 @@ from sage.structure.coerce_maps import CallableConvertMap
class ParametricRealFieldFrozenError(ValueError):
pass
+
class ParametricRealFieldInconsistencyError(ValueError):
pass
+
class ParametricRealFieldRefinementError(ValueError):
pass
+
from contextlib import contextmanager
allow_refinement_default = True
@@ -156,7 +159,7 @@ class ParametricRealField(Field):
sage: f[0]*f[1] <= 4
True
- Test-point free descriptions can be written which every comparison is assumed to be true.
+ Test-point free descriptions can be written which every comparison is assumed to be true.
MUCH slower because of many more polynoial evaluations via libsingular::
sage: K. = ParametricRealField(None, mutable_values=True)
@@ -205,7 +208,7 @@ class ParametricRealField(Field):
self._big_cells = big_cells
self._zero_element = ParametricRealFieldElement(self, 0)
- self._one_element = ParametricRealFieldElement(self, 1)
+ self._one_element = ParametricRealFieldElement(self, 1)
## REFACTOR: Maybe replace this by an instance of BasicSemialgebraicSet_eq_lt_le_sets - but careful - this class right now assumes polynomials
self._eq = set([])
self._lt = set([])
@@ -307,7 +310,7 @@ class ParametricRealField(Field):
TypeError: unsupported operand parent(s)...
Test that real number field elements can be upgraded to ``ParametricRealFieldElement``s.
- Note that this requires setting up the ParametricRealField with a specific base ring,
+ Note that this requires setting up the ParametricRealField with a specific base ring,
because there is no common parent of QQ(x) and a RealNumberField``::
sage: sqrt2, = nice_field_values([sqrt(2)])
@@ -319,7 +322,7 @@ class ParametricRealField(Field):
"""
if isinstance(S, ParametricRealField) and self is not S:
return None
- if S is sage.interfaces.mathematica.MathematicaElement or isinstance(S, RealNumberField_absolute) or isinstance(S, RealNumberField_quadratic) or AA.has_coerce_map_from(S):
+ if S is sage.interfaces.mathematica.MathematicaElement or isinstance(S, RealNumberField_absolute) or isinstance(S, RealNumberField_quadratic) or AA.has_coerce_map_from(S):
# Does the test with MathematicaElement actually work?
# We test whether S coerces into AA. This rules out inexact fields such as RDF.
return True
@@ -555,29 +558,16 @@ class ParametricRealField(Field):
except TypeError: # 'None' components
pass
raise FactorUndetermined("{} cannot be evaluated because the test point is not complete".format(fac))
-
+
def _partial_eval_factor(self, fac):
"""
Partially evaluate ``fac`` on the test point.
-
+
This function is only intended to be called after ``FactorUndetermined`` is raised from ``_eval_factor``.
"""
val_dict = {sym:val for sym, val in zip(fac.parent().gens() , self._values) if val is not None}
return fac.subs(val_dict)
-# Returns a symbolic expression or raises an ``EvaluationSuccessfulFlag``.
-# Receiving an ``EvaluationSuccessfulFlag`` means ``fac`` can be evaluated with the known values of the
-# test point.
-# base_ring = self._sym_field.base_ring()
-# if fac in base_ring:
-# raise EvaluationSuccessfulFlag("{} can be evaluated in the base_ring. Use _eval_factor instead.".format(fac))
-# try:
-# fac(self._values)
-# raise EvaluationSuccessfulFlag("{} can be evaluated with the test point. Use _eval_factor instead.".format(fac))
-# except TypeError:
-# val_dict = {sym:val for sym, val zip([symb.sym() for symb in self._gens], self._values) if val is not None}
-# return fac.subs(val_dict)
-
def _factor_sign(self, fac):
"""
Determine the sign of ``fac`` evaluated on the test point.
@@ -870,7 +860,7 @@ class ParametricRealField(Field):
else:
raise ParametricRealFieldInconsistencyError("New constraint {} {} {} is not satisfied by the test point".format(lhs, op, rhs))
else: #A numerical evaluation of the expression has failed. Assume the partial evaluation of the expression holds.
- comparison_val_or_expr = self._partial_eval_factor(comparison)
+ comparison_val_or_expr = self._partial_eval_factor(comparison)
if comparison_val_or_expr in base_ring:
if not op(comparison_val_or_expr, 0): #The partial evaluation is ture, means
raise ParametricRealFieldInconsistencyError("New constant constraint {} {} {} is not satisfied".format(lhs, op, rhs))
@@ -906,7 +896,8 @@ class ParametricRealField(Field):
return
if len(factors) == 1 and factors[0][1] == 1 and comparison_val is not None:
the_fac, d = factors[0]
- the_sign = sign(factors.unit() * comparison_val)
+ the_sign = sign(factors.unit() * comparison_val)
+
def factor_sign(fac):
if fac == the_fac:
return the_sign
@@ -1130,7 +1121,7 @@ class ParametricRealField(Field):
def make_proof_cell(self, **opt):
r"""
Make a :class:`SemialgebraicComplexComponent` from a :class:`ParametricRealField`.
-
+
In **opt, one can provide: region_type, function, find_region_type, default_var_bound, bddbsa, kwds_dict.
EXAMPLES::
@@ -1232,8 +1223,10 @@ def find_polynomial_map(eqs=[], poly_ring=None):
# Functions with ParametricRealField K
######################################
+
from sage.misc.sageinspect import sage_getargspec, sage_getvariablename
+
def read_default_args(function, **opt_non_default):
r"""
Return the default values of arguments of the function.
@@ -1261,7 +1254,7 @@ def read_default_args(function, **opt_non_default):
default_args = {}
if defaults is not None:
for i in range(len(defaults)):
- default_args[args[-i-1]]=defaults[-i-1]
+ default_args[args[-i-1]] = defaults[-i-1]
for (opt_name, opt_value) in opt_non_default.items():
if opt_name in default_args:
default_args[opt_name] = opt_value
@@ -1422,7 +1415,7 @@ class SemialgebraicComplexComponent(SageObject): # FIXME: Rename this to be m
else:
ptcolor = 'white'
if (xmin <= pt[0] <= xmax) and (ymin <= pt[1] <= ymax):
- g += point(pt, color = ptcolor, size = 2, zorder=10)
+ g += point(pt, color=ptcolor, size=2, zorder=10)
return g
def find_neighbour_candidates(self, flip_ineq_step, wall_crossing_method='heuristic', goto_lower_dim=False, pos_poly=None):
@@ -1469,7 +1462,7 @@ class SemialgebraicComplexComponent(SageObject): # FIXME: Rename this to be m
pt = find_point_flip_ineq_heuristic(self.var_value, l, list(bsa.lt_poly())+list(bsa.le_poly()), flip_ineq_step)
if pt is not None:
# Find a new point, use polynomial map to recover the values of those eliminated variables.
- pt_across_wall = tuple(p(pt) for p in self.polynomial_map)
+ pt_across_wall = tuple(p(pt) for p in self.polynomial_map)
if wall_crossing_method == 'mathematica' or wall_crossing_method == 'heuristic_then_mathematica' and (pt_across_wall is None):
bsa_mathematica = bsa.formal_relint(bsa_class='mathematica') # was BasicSemialgebraicSet_mathematica.from_bsa(bsa)
bsa_mathematica.add_polynomial_constraint(l, operator.gt)
@@ -1556,7 +1549,9 @@ class SemialgebraicComplexComponent(SageObject): # FIXME: Rename this to be m
if bsa_section.upstairs()._polyhedron.is_empty():
has_pt_on_wall = False
else:
- lts = []; les = []; eqs = []
+ lts = []
+ les = []
+ eqs = []
for ll in list(bsa_section.lt_poly()):
factors = ll.factor()
if len(factors) == 1:
@@ -1684,7 +1679,7 @@ class ProofCell(SemialgebraicComplexComponent, Classcall):
sage: C_copy._init_args == C._init_args
True
- (We do not test for equality C_copy == C -- we have not even decided yet what the semantics
+ (We do not test for equality C_copy == C -- we have not even decided yet what the semantics
of equality of bsa is.)
"""
@@ -1794,8 +1789,10 @@ class ProofCell(SemialgebraicComplexComponent, Classcall):
super(ProofCell, self).__init__(K, region_type, bddbsa, polynomial_map)
self.family = family
+
from collections import OrderedDict
+
class SemialgebraicComplex(SageObject):
r"""
A proof complex for parameter space analysis.
@@ -1829,7 +1826,7 @@ class SemialgebraicComplex(SageObject):
sage: complex.is_complete() # optional - mathematica
True
-
+
Example with non-linear wall::
sage: complex = SemialgebraicComplex(lambda x,y: max(x,y^2), ['x','y'], find_region_type=result_symbolic_expression, default_var_bound=(-3,3)) # optional - mathematica
@@ -1924,7 +1921,7 @@ class SemialgebraicComplex(SageObject):
to a "type" of the parameter region, for example:
- :func:`find_region_type_igp` (the default). The result of the computation is a Gomory-Johnson
- function `h`; it is passed to :func:`find_region_type_igp` as 2nd arg,
+ function `h`; it is passed to :func:`find_region_type_igp` as 2nd arg,
and then :func:`find_region_type_igp`which classifies the region of the
parameter by returning one of the strings
``'is_constructible'``, ``'not_constructible'``,
@@ -2003,7 +2000,7 @@ class SemialgebraicComplex(SageObject):
r"""
Return a random point that satisfies var_bounds and is in self.bsa.
- - If var_bounds is not specified, self.default_var_bound is taken.
+ - If var_bounds is not specified, self.default_var_bound is taken.
- var_bounds can be a list of 2-tuples whose length equals to the number of parameters, or lambda functions.
- It is used in random shooting method for functions like ``dg_2_step_mir``, which involve floor/ceil operations. We try to plot one layer for each n = floor(...) and superimpose the layers at the end to get the whole picture.
@@ -2029,11 +2026,11 @@ class SemialgebraicComplex(SageObject):
x = QQ(uniform(self.default_var_bound[0], self.default_var_bound[1]))
else:
if hasattr(var_bounds[i][0], '__call__'):
- l = var_bounds[i][0](*var_value)
+ l = var_bounds[i][0](*var_value)
else:
l = var_bounds[i][0]
if hasattr(var_bounds[i][1], '__call__'):
- u = var_bounds[i][1](*var_value)
+ u = var_bounds[i][1](*var_value)
else:
u = var_bounds[i][1]
if l > u:
@@ -2094,12 +2091,12 @@ class SemialgebraicComplex(SageObject):
for c in self.components:
if var_value in c.bsa:
yield c
-
+
def find_uncovered_random_point(self, var_bounds=None, max_failings=10000):
r"""
Return a random point that satisfies the bounds and is uncovered by any cells in the complex.
Return ``None`` if the number of attemps > max_failings.
-
+
EXAMPLES::
sage: from cutgeneratingfunctionology.igp import *
@@ -2128,7 +2125,7 @@ class SemialgebraicComplex(SageObject):
The argument formal_closure whether inequalities are treated as <= 0 or as < 0.
If such point does not exist, return ``None``.
-
+
EXAMPLES::
sage: from cutgeneratingfunctionology.igp import *
@@ -2205,18 +2202,18 @@ class SemialgebraicComplex(SageObject):
self.points_to_test[num_eq] = OrderedDict()
if not num_eq in self.tested_points:
self.tested_points[num_eq] = set([])
- new_component = self._cell_class(self.family, var_value,
+ new_component = self._cell_class(self.family, var_value,
find_region_type=self.find_region_type, bddbsa=bddbsa, polynomial_map=polynomial_map)
new_num_eq = len(list(new_component.bsa.eq_poly()))
- if new_num_eq > num_eq:
- logging.warning("The cell around %s defined by %s has more equations than boundary %s" %(new_component.var_value, new_component.bsa, bddbsa))
+ if new_num_eq > num_eq:
+ logging.warning("The cell around %s defined by %s has more equations than boundary %s" % (new_component.var_value, new_component.bsa, bddbsa))
#import pdb; pdb.set_trace()
- # bsa is lower dimensional as it has more equations than bddbsa,
+ # bsa is lower dimensional as it has more equations than bddbsa,
# so we try to perturb the testpoint to obtain a
# new testpoint in bddbsa that does not fall into a lower dimensional cell.
# Heuristic code using gradient desecent. #FIXME.
- for l in (set(new_component.bsa.eq_poly())- set(bddbsa.eq_poly())):
+ for l in (set(new_component.bsa.eq_poly()) - set(bddbsa.eq_poly())):
ineqs = list(new_component.bddbsa.lt_poly())+list(new_component.bddbsa.le_poly())
pts = [find_point_flip_ineq_heuristic(var_value, l, ineqs, 1/2017), find_point_flip_ineq_heuristic(var_value, -l, ineqs, 1/2017)]
for pt in pts:
@@ -2277,7 +2274,7 @@ class SemialgebraicComplex(SageObject):
Plot the complex and store the graph.
- If restart is ``False``, plot the newly added cells on top of the last graph; otherwise, start a new graph.
- - If slice_value is given, it is either a polynomial_map that defines a section, or a list of fixed parameter values with two of them being None. Plot the section.
+ - If slice_value is given, it is either a polynomial_map that defines a section, or a list of fixed parameter values with two of them being None. Plot the section.
- plot_points controls the quality of the plotting.
EXAMPLES::
@@ -2333,7 +2330,7 @@ class SemialgebraicComplex(SageObject):
# # FIXME: zorder is broken in region_plot/ContourPlot.
# for c in self.components[self.num_plotted_components::]:
# num_eq = len(list(c.bsa.eq_poly()))
- # gc = c.plot(alpha=alpha, plot_points=plot_points, slice_value=slice_value, default_var_bound=self.default_var_bound, goto_lower_dim=goto_lower_dim, zorder=num_eq, **kwds)
+ # gc = c.plot(alpha=alpha, plot_points=plot_points, slice_value=slice_value, default_var_bound=self.default_var_bound, goto_lower_dim=goto_lower_dim, zorder=num_eq, **kwds)
# if gc: # need this because (empty g + empty gc) forgets about xmin xmax ymin ymax.
# self.graph += gc
# Workaround.
@@ -2355,11 +2352,11 @@ class SemialgebraicComplex(SageObject):
new_bsa.add_polynomial_constraint(l, operator.eq)
self.graph += new_bsa.plot(alpha=alpha, plot_points=plot_points, slice_value=slice_value, color=color, fill_color=color, xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax)
for c in components:
- if len(list(c.bsa.eq_poly()))==1:
+ if len(list(c.bsa.eq_poly())) == 1:
self.graph += c.plot(alpha=alpha, plot_points=plot_points, slice_value=slice_value, default_var_bound=self.default_var_bound, goto_lower_dim=False, zorder=0, **kwds)
if goto_lower_dim:
for c in components:
- if len(list(c.bsa.eq_poly()))==1:
+ if len(list(c.bsa.eq_poly())) == 1:
color = find_region_color(c.region_type)
for l in c.bsa.lt_poly():
new_bsa = BasicSemialgebraicSet_eq_lt_le_sets(eq=list(c.bsa.eq_poly())+[l], lt=[ll for ll in c.bsa.lt_poly() if ll != l], le=list(c.bsa.le_poly()))
@@ -2369,9 +2366,9 @@ class SemialgebraicComplex(SageObject):
new_bsa.add_polynomial_constraint(l, operator.eq)
self.graph += new_bsa.plot(alpha=alpha, plot_points=plot_points, slice_value=slice_value, color=color, fill_color=color, xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax)
for c in components:
- if len(list(c.bsa.eq_poly()))==2:
+ if len(list(c.bsa.eq_poly())) == 2:
ptcolor = find_region_color(c.region_type)
- self.graph += point(c.var_value, color = ptcolor, zorder=10)
+ self.graph += point(c.var_value, color=ptcolor, zorder=10)
self.num_plotted_components = len(self.components)
return self.graph
@@ -2408,7 +2405,7 @@ class SemialgebraicComplex(SageObject):
- var_value: the starting point. If not given, start with a random point.
- flip_ineq_step: a small positive number that controls the step length in wall-crossing.
- check_completion: When check_completion is ``True``, after bfs terminates, check whether the entire parameter space is covered by cells, using Mathematica's ``FindInstance`` (if max_failings=0) or random shooting (if max_failings>0). If an uncovered point has been found, restart the bfs from this point.
- - wall_crossing_method: 'mathematica' or 'heuristic' or 'heuristic_then_mathematica'
+ - wall_crossing_method: 'mathematica' or 'heuristic' or 'heuristic_then_mathematica'
- wall_crossing_method='heuristic_then_mathematica': try heuristic method first. If it does not find a new testpoint, then use Mathematica.
- goto_lower_dim: whether lower dimensional cells are considered. If goto_lower_dim is ``False`` or if goto_lower_dim is ``True`` and wall_crossing method is 'heuristic' but the wall is non-linear, then find new testpoint across the wall only.
@@ -2462,10 +2459,10 @@ class SemialgebraicComplex(SageObject):
uncovered_pt = self.find_uncovered_random_point(max_failings=max_failings)
if uncovered_pt is not None:
logging.warning("After bfs, the complex has uncovered point %s." % (uncovered_pt,))
- self.bfs_completion(var_value=uncovered_pt, \
- flip_ineq_step=flip_ineq_step, \
- check_completion=check_completion, \
- wall_crossing_method=wall_crossing_method, \
+ self.bfs_completion(var_value=uncovered_pt,
+ flip_ineq_step=flip_ineq_step,
+ check_completion=check_completion,
+ wall_crossing_method=wall_crossing_method,
goto_lower_dim=goto_lower_dim)
def is_complete(self, formal_closure=False):
@@ -2567,9 +2564,9 @@ def gradient(ineq):
[3*z^2 + 6*z]
"""
if hasattr(ineq, 'gradient'):
- return ineq.gradient()
+ return ineq.gradient()
else:
- return [ineq.derivative()]
+ return [ineq.derivative()]
####################################
# Find region type and region color
@@ -2702,8 +2699,8 @@ def return_result(field, result):
def result_concrete_value(field, result):
r"""
Return the concrete values in result as a tuple. See also ``result_symbolic_expression()``.
-
- This function can provided to ``find_region_type`` when setting up a :class:`SemialgebraicComplex`.
+
+ This function can provided to ``find_region_type`` when setting up a :class:`SemialgebraicComplex`.
In this way, one can compare result of type :class:`ParametricRealFieldElement` or list of :class:`ParametricRealFieldElement`
with the previous elements in ``region_type_color_map`` which do not necessarily have the same parent.
@@ -2725,8 +2722,8 @@ def result_concrete_value(field, result):
def result_symbolic_expression(field, result):
r"""
Return the symbolic expressions in result as a tuple
-
- This function can provided to ``find_region_type`` when setting up a :class:`SemialgebraicComplex`.
+
+ This function can provided to ``find_region_type`` when setting up a :class:`SemialgebraicComplex`.
In this way, one can compare result of type :class:`ParametricRealFieldElement` or list of :class:`ParametricRealFieldElement`
with the previous elements in ``region_type_color_map`` which do not necessarily have the same parent.
@@ -2750,7 +2747,7 @@ def result_symbolic_expression(field, result):
sage: vol1 == vol2
False
sage: sym_exp1 == sym_exp2
- True
+ True
"""
symbolic_expression = tuple(elt._sym if hasattr(elt, '_sym') else elt for elt in flatten([result]))
return symbolic_expression
@@ -2775,7 +2772,7 @@ def find_region_type_igp_extreme_big_cells(K, h):
h = copy(hcopy)
for x in h.values_at_end_points():
if (x < 0) or (x > 1):
- is_extreme = False
+ is_extreme = False
break
if not is_extreme:
assert (x < 0) or (x > 1)
@@ -2821,7 +2818,7 @@ def find_region_type_igp_extreme_big_cells(K, h):
ucs = generate_uncovered_components(h)
f = find_f(h)
for uncovered_pt in [f/2, (f+1)/2]:
- if any((i[0] == uncovered_pt or i[1] == uncovered_pt) for uc in ucs for i in uc if len(uc)==2):
+ if any((i[0] == uncovered_pt or i[1] == uncovered_pt) for uc in ucs for i in uc if len(uc) == 2):
uncovered_pts = [uncovered_pt]
is_extreme = False
break
@@ -2855,7 +2852,8 @@ def find_region_type_igp_extreme_big_cells(K, h):
return False
return True
-region_type_color_map = [('not_constructible', 'lightgrey'), ('is_constructible', 'black'), ('not_minimal', 'orange'), ('is_minimal', 'darkgrey'),('not_extreme', 'green'), ('is_extreme', 'blue'), ('stop', 'grey'), (True, 'blue'), (False, 'red'), ('constructible', 'darkgrey'), ('extreme', 'red')]
+
+region_type_color_map = [('not_constructible', 'lightgrey'), ('is_constructible', 'black'), ('not_minimal', 'orange'), ('is_minimal', 'darkgrey'), ('not_extreme', 'green'), ('is_extreme', 'blue'), ('stop', 'grey'), (True, 'blue'), (False, 'red'), ('constructible', 'darkgrey'), ('extreme', 'red')]
def find_region_color(region_type):
r"""
@@ -2900,11 +2898,11 @@ def color_of_ith_region_type(i):
def find_point_flip_ineq_heuristic(current_var_value, ineq, ineqs, flip_ineq_step):
r"""
- The current_var_value satisfies that l(current_var_value) <= 0 for l=ineq and for every l in ineqs,
+ The current_var_value satisfies that l(current_var_value) <= 0 for l=ineq and for every l in ineqs,
where ineq is a polynomial and ineqs is a list of polynomials.
Use heuristic method (gradient descent method with given small positive step length flip_ineq_step)
- to find a new_point (type is tuple) such that
+ to find a new_point (type is tuple) such that
ineq(new_point) > 0, l(new_point) < 0 for all l in ineqs
Return new_point, or ``None`` if it fails to find one.
@@ -3010,7 +3008,7 @@ def find_point_flip_ineq_heuristic(current_var_value, ineq, ineqs, flip_ineq_ste
ineq_value = ineq(*current_point)
try_before_fail -= 1
# print (current_point, RR(ineq_value))
- try_before_fail = max(ceil(2/flip_ineq_step), 2000) # define maximum number of walks. Considered ceil(-2 * ineq_value /flip_ineq_step) but it is too slow in the impossible cases. Added a loop with 2000 times step length when ineq_value is very negative.
+ try_before_fail = max(ceil(2/flip_ineq_step), 2000) # define maximum number of walks. Considered ceil(-2 * ineq_value /flip_ineq_step) but it is too slow in the impossible cases. Added a loop with 2000 times step length when ineq_value is very negative.
while (ineq_value <= 1e-10) and (try_before_fail > 0):
ineq_direction = vector(g(*current_point) for g in ineq_gradient)
if ineq.degree() == 1:
@@ -3034,8 +3032,8 @@ def find_point_flip_ineq_heuristic(current_var_value, ineq, ineqs, flip_ineq_ste
def adjust_pt_to_satisfy_ineqs(current_point, ineq, strict_ineqs, nonstrict_ineqs, flip_ineq_step):
r"""
- Walk from current_point (type=vector) in the direction perpendicular to
- the gradient of ineq with small positive step length flip_ineq_step,
+ Walk from current_point (type=vector) in the direction perpendicular to
+ the gradient of ineq with small positive step length flip_ineq_step,
while maintaining the value of ineq(*current_point) which is >= 0.
until get a new point such that l(new point)<0 for all l in strict_ineqs and l(new point)<=0 for all l in nonstrict_ineqs
@@ -3057,7 +3055,7 @@ def adjust_pt_to_satisfy_ineqs(current_point, ineq, strict_ineqs, nonstrict_ineq
sage: pt = adjust_pt_to_satisfy_ineqs(vector([11/8, 7/8]), a+b-2, [-a+b^2], [a-1], 1/4); pt is None
True
- Bug example in cpl Cell 9 with test point (499/1250, 488072439572/4866126017667). Without converting input to QQ, output was (0.333000000000000, 0.111333333333333) with -2*f - 3*z + 1 = -5.55111512312578e-17 , the QQ of the output=(333/1000, 167/1500) has -2*f - 3*z + 1 == 0. Revise the code to take QQ input current point::
+ Bug example in cpl Cell 9 with test point (499/1250, 488072439572/4866126017667). Without converting input to QQ, output was (0.333000000000000, 0.111333333333333) with -2*f - 3*z + 1 = -5.55111512312578e-17 , the QQ of the output=(333/1000, 167/1500) has -2*f - 3*z + 1 == 0. Revise the code to take QQ input current point::
sage: P.=QQ[]
sage: current_point = vector([0.333000000000000, 0.100300000000000]); ineq = -3*f + 1; strict_ineqs = [2*f + 2*z - 1, f + 5*z - 1, -f - 6*z + 1, -2*f - 3*z + 1]; nonstrict_ineqs = []; flip_ineq_step = 1/1000
@@ -3076,8 +3074,8 @@ def adjust_pt_to_satisfy_ineqs(current_point, ineq, strict_ineqs, nonstrict_ineq
Bug example in cpl bigcell 16 with test point (12219/26000, 24/1625). Redo with QQ had infinite loop. Bug comes from find_neighbour_point where it calls bsa_section.upstairs()._polyhedron.is_empty(), which is not strong enough. If we could test bsa_section is empty (perhaps by tighten_upstairs_by_mccormick), then this example should not appear::
- sage: P.=QQ[];
- sage: current_point = vector((71582788/143165577, 4673/377000)) # came from vector((RR(70727/150800), RR(4673/377000))),
+ sage: P.=QQ[];
+ sage: current_point = vector((71582788/143165577, 4673/377000)) # came from vector((RR(70727/150800), RR(4673/377000))),
sage: ineq=None; strict_ineqs=[2*f - 1, -9*f + 2]; nonstrict_ineqs=[4*f^2 - 4*f + 1]; flip_ineq_step=1/1000
sage: pt = adjust_pt_to_satisfy_ineqs(current_point, None, strict_ineqs, nonstrict_ineqs, flip_ineq_step=1/1000); pt is None #long time
True
@@ -3092,7 +3090,7 @@ def adjust_pt_to_satisfy_ineqs(current_point, ineq, strict_ineqs, nonstrict_ineq
#current_point is a vector
if ineq is not None:
ineq_gradient = gradient(ineq)
- if all(x.parent()==QQ for x in current_point):
+ if all(x.parent() == QQ for x in current_point):
max_walks = min(ceil(2/flip_ineq_step), 20)
else:
max_walks = min(ceil(2/flip_ineq_step), 200) #1000? # define maximum number of walks.
@@ -3154,7 +3152,7 @@ def adjust_pt_to_satisfy_ineqs(current_point, ineq, strict_ineqs, nonstrict_ineq
return None
if ineq is not None and ineq(*current_point) < 0:
return None
- if all(x.parent()==QQ for x in current_point):
+ if all(x.parent() == QQ for x in current_point):
return tuple(current_point)
else:
prec = 30 # We hope to have small denominator for the new point, so we set precision in bits = 30 is about 8 digits.
@@ -3215,13 +3213,14 @@ def embed_function_into_family(given_function, parametric_family, check_completi
var_name = []
var_value = []
for (name, value) in default_args.items():
- if not isinstance(value, bool) and not value is None:
+ if not isinstance(value, bool) and value is not None:
try:
RR(value)
var_name.append(name)
var_value.append(value)
except:
pass
+
def frt(K, h):
if h is None:
return False
@@ -3264,6 +3263,7 @@ def embed_function_into_family(given_function, parametric_family, check_completi
# plot_cpl_components(complex.components)
return {}
+
"""
EXAMPLES::
diff --git a/cutgeneratingfunctionology/spam/parametric_real_field_element.py b/cutgeneratingfunctionology/spam/parametric_real_field_element.py
index 39c6375b0..8c6e4c831 100644
--- a/cutgeneratingfunctionology/spam/parametric_real_field_element.py
+++ b/cutgeneratingfunctionology/spam/parametric_real_field_element.py
@@ -12,6 +12,7 @@
from cutgeneratingfunctionology.shared.EvaluationExceptions import FactorUndetermined
import operator
+
def richcmp_op_negation(op):
if op == op_LT:
return op_GE
@@ -28,6 +29,7 @@ def richcmp_op_negation(op):
else:
raise ValueError("{} is not a valid richcmp operator".format(op))
+
def format_richcmp_op(op):
if op == op_LT:
return '<'
@@ -44,6 +46,7 @@ def format_richcmp_op(op):
else:
raise ValueError("{} is not a valid richcmp operator".format(op))
+
class ParametricRealFieldElement(FieldElement):
r"""
A :class:`ParametricRealFieldElement` stores a symbolic expression of the parameters in the problem and a concrete value, which is the evaluation of this expression on the given parameter tuple.
@@ -85,7 +88,8 @@ def val(self):
if possible_val in possible_val.base_ring():
return possible_val
else:
- raise FactorUndetermined
+ raise FactorUndetermined.("{} cannot be evaluated because the test point is not complete".format(self.sym()))
+
def _richcmp_(left, right, op):
r"""
Examples for traditional cmp semantics::
@@ -135,7 +139,7 @@ def _richcmp_(left, right, op):
if left.parent()._big_cells:
try:
result = richcmp((left-right).val(), 0, op)
- except FactorUndetermined: # Partial evauation is happen, assume the result is True.
+ except FactorUndetermined: # Partial evaluation has happen, assume the result is True.
result = True
if result:
true_op = op
@@ -170,13 +174,8 @@ def _richcmp_(left, right, op):
left.parent().assume_comparison(right, operator.lt, left)
return richcmp(left.val(), right.val(), op)
except FactorUndetermined:
- result = True
- if result:
+ # With a partial evaluation, assume the written inequality is true.
true_op = op
- else:
- true_op = richcmp_op_negation(op)
- # left.sym() - right.sym() may cancel denominators, but that is
- # OK because _div_ makes sure that denominators are nonzero.
if true_op == op_LT:
left.parent().assume_comparison(left - right, operator.lt)
elif true_op == op_GT:
@@ -193,8 +192,6 @@ def _richcmp_(left, right, op):
raise ValueError("{} is not a valid richcmp operator".format(op))
return True
-
-
def __abs__(self):
"""
Examples for traditional cmp semantics::
@@ -436,6 +433,7 @@ def __hash__(self):
"""
return hash(self.val())
+
def is_parametric_element(x):
# We avoid using isinstance here so that this is robust even if parametric.sage is reloaded.
# For example, apparently in the test suite.
From cdec3807ecae56534d6080721d25010f4a1ed69f Mon Sep 17 00:00:00 2001
From: ComboProblem <102884863+ComboProblem@users.noreply.github.com>
Date: Tue, 30 Sep 2025 10:18:16 -0700
Subject: [PATCH 34/37] spelling fixes
---
.../igp/parametric.sage | 26 +++++++++++--------
1 file changed, 15 insertions(+), 11 deletions(-)
diff --git a/cutgeneratingfunctionology/igp/parametric.sage b/cutgeneratingfunctionology/igp/parametric.sage
index 4b0734922..a8b0f9aa3 100644
--- a/cutgeneratingfunctionology/igp/parametric.sage
+++ b/cutgeneratingfunctionology/igp/parametric.sage
@@ -862,9 +862,9 @@ class ParametricRealField(Field):
else: #A numerical evaluation of the expression has failed. Assume the partial evaluation of the expression holds.
comparison_val_or_expr = self._partial_eval_factor(comparison)
if comparison_val_or_expr in base_ring:
- if not op(comparison_val_or_expr, 0): #The partial evaluation is ture, means
+ if not op(comparison_val_or_expr, 0):
raise ParametricRealFieldInconsistencyError("New constant constraint {} {} {} is not satisfied".format(lhs, op, rhs))
- else: # comparision_val_or_expr is algebraic expresion, asume the comparision here is comparionsion_val_or_expr
+ else: # comparision_val_or_expr is algebraic expression, assume the comparison here is comparionsion_val_or_expr.
comparison = comparison_val_or_expr
if comparison in base_ring:
return
@@ -1161,7 +1161,7 @@ class ParametricRealField(Field):
###############################
def find_polynomial_map(eqs=[], poly_ring=None):
"""
- BAD FUCNTION! It is used in 'mathematica' approach for non-linear case. Can we avoid it?
+ BAD FUNCTION! It is used in 'mathematica' approach for non-linear case. Can we avoid it?
Return a polynomial map that eliminates linear variables in eqs, and a dictionary recording which equations were used to eliminate those linear variables.
Assume that gaussian elimination has been performed by PPL.minimized_constraints() on the input list of equations eqs.
It is only called in SemialgebraicComplex.add_new_component in the case polynomial_map is not provided but bddbsa has equations.
@@ -1313,7 +1313,7 @@ class SemialgebraicComplexComponent(SageObject): # FIXME: Rename this to be m
sage: sorted(component.bsa.lt_poly())
[-x, 3*x - 4]
- In ProofCell region_type should alreay consider the polynomial_map::
+ In ProofCell region_type should already consider the polynomial_map::
sage: K. = ParametricRealField([1,1/2])
sage: assert(x == 2*y)
@@ -1344,10 +1344,14 @@ class SemialgebraicComplexComponent(SageObject): # FIXME: Rename this to be m
# In lower dim proof cell or non-linear equations case, some equations of K._bsa are not presented in polynomial_map.
eqs = list(K._bsa.eq_poly())
if not all(l(polynomial_map) == 0 for l in eqs):
- polynomial_map = find_polynomial_map(eqs=eqs, poly_ring=poly_ring)
+ polynomial_map = find_polynomial_map(eqs=eqs, poly_ring=poly_ring)fv
#self.bsa = K._bsa.section(polynomial_map, bsa_class='veronese', poly_ring=poly_ring) # this is a bigger_bsa
self.bsa = BasicSemialgebraicSet_veronese.from_bsa(BasicSemialgebraicSet_local(K._bsa.section(polynomial_map, poly_ring=poly_ring), self.var_value)) # TODO:, polynomial_map=list(poly_ring.gens()))
- # WHY is this input polynomial_map sometimes not compatible with the variable elimination done in bddbsa? Because upstairs ppl bsa eliminates large x_i in the inequalities, and x_i doesn't necessarily correspond to the i-th variable in poly_ring. Since polynomial_map and v_dict were not given at the initialization of veronese, the variable first encounted in the constraints is considered as x0 by upstairs ppl bsa. # In old code, we fixed the order of upstairs variables by adding initial space dimensions. We don't do that in the current code. Instead, we take the section of bddbsa to eliminate the varibles in the equations. # Is the given bddbsa required to be veronese with upstairs being ppl_bsa? Convert it anyway. # It's the same as BasicSemialgebraicSet_veronese.from_bsa(bddbsa.section(self.polynomial_map), poly_ring=poly_ring)
+ # WHY is this input polynomial_map sometimes not compatible with the variable elimination done in bddbsa?
+ # Because upstairs ppl bsa eliminates large x_i in the inequalities, and x_i doesn't necessarily correspond to the i-th variable in poly_ring.
+ # Since polynomial_map and v_dict were not given at the initialization of veronese, the variable first encountered, in the constraints is considered as x0 by upstairs ppl bsa.
+ # In old code, we fixed the order of upstairs variables by adding initial space dimensions. We don't do that in the current code. Instead, we take the section of bddbsa to eliminate the variables in the equations.
+ # Is the given bddbsa required to be veronese with upstairs being ppl_bsa? Convert it anyway. # It's the same as BasicSemialgebraicSet_veronese.from_bsa(bddbsa.section(self.polynomial_map), poly_ring=poly_ring)
self.bddbsa = BasicSemialgebraicSet_veronese.from_bsa(BasicSemialgebraicSet_local(bddbsa.section(polynomial_map, poly_ring=poly_ring), self.var_value))
# Taking section forgets the equations. Then add back the equations # Finally self.bsa should be the same as K._bsa, but its inequalities don't have variables eliminated by polynomial map, so that heuristic wall crossing can be done later.
for i in range(len(self.var_name)):
@@ -1427,13 +1431,13 @@ class SemialgebraicComplexComponent(SageObject): # FIXME: Rename this to be m
- if goto_lower_dim=False, the cell is considered as its formal closure, so no recursion into test points in lower dimensional cells.
- pos_poly is a polynomial. The return test point must satisfy pos_poly(new test point) > 0.
- OUTPUT new_points is a dictionary of dictionaries. The new_points[i] is a dictionay whose keys = candidate neighbour testpoints, values = (bddbsa whose eq_poly has i elements, polynomial_map, no_crossing_l) of the candidate neighbour cell that contains the candidate neighbour testpoint. bddbsa is recorded so that (1) complex.bddbsa is always respected; and (2) can recursively go into lower dimensional cells. polynomial_map is recorded and passed to the constructor of the neighbour cell. no_crossing is passed to the neighour cell for its find_neighbour_candidates method. We no longer update self.bsa by removing (obvious) redundant eq, lt, le constraints from its description at the end, even when 'mathematica' is used.
+ OUTPUT new_points is a dictionary of dictionaries. The new_points[i] is a dictionary whose keys = candidate neighbour testpoints, values = (bddbsa whose eq_poly has i elements, polynomial_map, no_crossing_l) of the candidate neighbour cell that contains the candidate neighbour testpoint. bddbsa is recorded so that (1) complex.bddbsa is always respected; and (2) can recursively go into lower dimensional cells. polynomial_map is recorded and passed to the constructor of the neighbour cell. no_crossing is passed to the neighbour cell for its find_neighbour_candidates method. We no longer update self.bsa by removing (obvious) redundant eq, lt, le constraints from its description at the end, even when 'mathematica' is used.
"""
bsa_eq_poly = list(self.bsa.eq_poly())
bsa_le_poly = list(self.bsa.le_poly())
bsa_lt_poly = list(self.bsa.lt_poly())
num_eq = len(bsa_eq_poly) #was len(list(self.bddbsa.eq_poly()))
- new_points = {} #dictionary with key=num_eq, value=dictionay of pt: (bddbsa, polynomial_map).
+ new_points = {} #dictionary with key=num_eq, value=dictionary of pt: (bddbsa, polynomial_map).
#bddbsa = copy(self.bddbsa)
#for l in bsa_eq_poly: # should be already in bddbsa
# bddbsa.add_polynomial_constraint(l, operator.eq)
@@ -2095,7 +2099,7 @@ class SemialgebraicComplex(SageObject):
def find_uncovered_random_point(self, var_bounds=None, max_failings=10000):
r"""
Return a random point that satisfies the bounds and is uncovered by any cells in the complex.
- Return ``None`` if the number of attemps > max_failings.
+ Return ``None`` if the number of attempts > max_failings.
EXAMPLES::
@@ -2311,7 +2315,7 @@ class SemialgebraicComplex(SageObject):
self.graph.xmin(kwds['xmin'])
xmin = kwds['xmin']
else:
- xmin = self.default_var_bound[0] # special treatement in the case goto_lower_dim which uses bsa.plot() instead of component.plot() because zorder is broken in region_plot/ContourPlot.
+ xmin = self.default_var_bound[0] # special treatment in the case goto_lower_dim which uses bsa.plot() instead of component.plot() because zorder is broken in region_plot/ContourPlot.
if 'xmax' in kwds:
self.graph.xmax(kwds['xmax'])
xmax = kwds['xmax']
@@ -2988,7 +2992,7 @@ def find_point_flip_ineq_heuristic(current_var_value, ineq, ineqs, flip_ineq_ste
sage: all(l(pt) < 0 for l in ineqs) and ineq(pt)>0
True
- Bug examle from positive definite matrix [a, b; b, 1/4], where ineq is very negative at the test point. Make big moves first, then small moves::
+ Bug example from positive definite matrix [a, b; b, 1/4], where ineq is very negative at the test point. Make big moves first, then small moves::
sage: P.=QQ[]; current_var_value = (5, 4); ineq = -4*b^2 + a; ineqs = [-a]; flip_ineq_step=1/100
sage: pt = find_point_flip_ineq_heuristic(current_var_value, ineq, ineqs, flip_ineq_step); # got pt (30943/6018, 17803/15716)
From 9ee235871f284debf0574d5d66294338cd5bb3c3 Mon Sep 17 00:00:00 2001
From: ComboProblem <102884863+ComboProblem@users.noreply.github.com>
Date: Tue, 30 Sep 2025 16:21:30 -0700
Subject: [PATCH 35/37] minor fixes
---
cutgeneratingfunctionology/igp/parametric.sage | 2 +-
.../spam/parametric_real_field_element.py | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/cutgeneratingfunctionology/igp/parametric.sage b/cutgeneratingfunctionology/igp/parametric.sage
index a8b0f9aa3..5b44a94b2 100644
--- a/cutgeneratingfunctionology/igp/parametric.sage
+++ b/cutgeneratingfunctionology/igp/parametric.sage
@@ -1344,7 +1344,7 @@ class SemialgebraicComplexComponent(SageObject): # FIXME: Rename this to be m
# In lower dim proof cell or non-linear equations case, some equations of K._bsa are not presented in polynomial_map.
eqs = list(K._bsa.eq_poly())
if not all(l(polynomial_map) == 0 for l in eqs):
- polynomial_map = find_polynomial_map(eqs=eqs, poly_ring=poly_ring)fv
+ polynomial_map = find_polynomial_map(eqs=eqs, poly_ring=poly_ring)
#self.bsa = K._bsa.section(polynomial_map, bsa_class='veronese', poly_ring=poly_ring) # this is a bigger_bsa
self.bsa = BasicSemialgebraicSet_veronese.from_bsa(BasicSemialgebraicSet_local(K._bsa.section(polynomial_map, poly_ring=poly_ring), self.var_value)) # TODO:, polynomial_map=list(poly_ring.gens()))
# WHY is this input polynomial_map sometimes not compatible with the variable elimination done in bddbsa?
diff --git a/cutgeneratingfunctionology/spam/parametric_real_field_element.py b/cutgeneratingfunctionology/spam/parametric_real_field_element.py
index 8c6e4c831..a0cd5c55e 100644
--- a/cutgeneratingfunctionology/spam/parametric_real_field_element.py
+++ b/cutgeneratingfunctionology/spam/parametric_real_field_element.py
@@ -88,7 +88,7 @@ def val(self):
if possible_val in possible_val.base_ring():
return possible_val
else:
- raise FactorUndetermined.("{} cannot be evaluated because the test point is not complete".format(self.sym()))
+ raise FactorUndetermined("{} cannot be evaluated because the test point is not complete".format(self.sym()))
def _richcmp_(left, right, op):
r"""
From 77583771efee1804d84e05983c82e37113dcde7a Mon Sep 17 00:00:00 2001
From: ComboProblem <102884863+ComboProblem@users.noreply.github.com>
Date: Fri, 3 Oct 2025 09:47:20 -0700
Subject: [PATCH 36/37] well it works
---
cutgeneratingfunctionology/igp/parametric.sage | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/cutgeneratingfunctionology/igp/parametric.sage b/cutgeneratingfunctionology/igp/parametric.sage
index 5b44a94b2..293e1bd37 100644
--- a/cutgeneratingfunctionology/igp/parametric.sage
+++ b/cutgeneratingfunctionology/igp/parametric.sage
@@ -864,8 +864,10 @@ class ParametricRealField(Field):
if comparison_val_or_expr in base_ring:
if not op(comparison_val_or_expr, 0):
raise ParametricRealFieldInconsistencyError("New constant constraint {} {} {} is not satisfied".format(lhs, op, rhs))
- else: # comparision_val_or_expr is algebraic expression, assume the comparison here is comparionsion_val_or_expr.
+ else: # comparision_val_or_expr is symbolic expression, assume the comparison here is comparionsion_val_or_expr.
comparison = comparison_val_or_expr
+ self.record_factor(comparison, op)
+ return
if comparison in base_ring:
return
if comparison.denominator() == 1 and comparison.numerator().degree() == 1:
@@ -1063,7 +1065,9 @@ class ParametricRealField(Field):
eq_factors.append(new_fac)
except FactorUndetermined:
eq_factors.append(new_fac) #??? or pass?
- #This doesn't matter for now because under this branch self._allow_refinement=True, and testpoint value is not removed, so the exception FactorUndetermined should not happen. In the future if the code in big_cells_impl changes and this exception happens, it is still safer to eq_factors.append(new_fac). This could result in smaller cell (which is allowed). With "pass" it may raise error in corner cases, such as when the removed testpoint value was the only eq factor without which there would be a sign contradiction.
+ #This doesn't matter for now because under this branch self._allow_refinement=True, and testpoint value is not removed, so the exception FactorUndetermined should not happen.
+ #In the future if the code in big_cells_impl changes and this exception happens, it is still safer to eq_factors.append(new_fac). T
+ #This could result in smaller cell (which is allowed). With "pass" it may raise error in corner cases, such as when the removed testpoint value was the only eq factor without which there would be a sign contradiction.
for new_fac in lt_factors:
self.record_factor(new_fac, operator.le)
if not self._big_cells:
@@ -1071,7 +1075,7 @@ class ParametricRealField(Field):
self.record_factor(new_fac, operator.eq)
else: #self._big_cells is True, self._allow_refinement is True.
undecided_eq = []
- for new_fac in eq_factors:
+ for new_fac in eq_factors: # eq_factors is not guantreed to be non empty
if self.is_factor_known(new_fac, operator.le):
unit_sign = -unit_sign
elif not self.is_factor_known(-new_fac, operator.le):
From 547514437406051f4bd7e37cceb1b00ad58d1e74 Mon Sep 17 00:00:00 2001
From: ComboProblem <102884863+ComboProblem@users.noreply.github.com>
Date: Tue, 28 Oct 2025 10:38:04 -0700
Subject: [PATCH 37/37] change location of parametric exceptions
---
.../igp/parametric.sage | 23 ++++---------------
.../shared/EvaluationExceptions.py | 2 --
cutgeneratingfunctionology/spam/exceptions.py | 14 +++++++++++
.../spam/parametric_real_field_element.py | 2 +-
4 files changed, 20 insertions(+), 21 deletions(-)
delete mode 100644 cutgeneratingfunctionology/shared/EvaluationExceptions.py
create mode 100644 cutgeneratingfunctionology/spam/exceptions.py
diff --git a/cutgeneratingfunctionology/igp/parametric.sage b/cutgeneratingfunctionology/igp/parametric.sage
index 293e1bd37..b1a6bf2a8 100644
--- a/cutgeneratingfunctionology/igp/parametric.sage
+++ b/cutgeneratingfunctionology/igp/parametric.sage
@@ -20,8 +20,8 @@ from cutgeneratingfunctionology.spam.basic_semialgebraic_local import BasicSemia
from cutgeneratingfunctionology.spam.semialgebraic_mathematica import BasicSemialgebraicSet_mathematica, from_mathematica
from cutgeneratingfunctionology.spam.basic_semialgebraic_groebner_basis import BasicSemialgebraicSet_groebner_basis
from cutgeneratingfunctionology.spam.polyhedral_complex import PolyhedralComplex
-from cutgeneratingfunctionology.shared.EvaluationExceptions import FactorUndetermined
from .parametric_family import Classcall, ParametricFamily_base, ParametricFamily
+from cutgeneratingfunctionology.spam.exceptions import *
debug_new_factors = False
debug_cell_exceptions = False
@@ -48,18 +48,6 @@ import sage.rings.number_field.number_field_base as number_field_base
from sage.structure.coerce_maps import CallableConvertMap
-class ParametricRealFieldFrozenError(ValueError):
- pass
-
-
-class ParametricRealFieldInconsistencyError(ValueError):
- pass
-
-
-class ParametricRealFieldRefinementError(ValueError):
- pass
-
-
from contextlib import contextmanager
allow_refinement_default = True
@@ -601,15 +589,14 @@ class ParametricRealField(Field):
...
ParametricRealFieldInconsistencyError: New constant constraint...
- TESTS for consistency checks when testpoint values is None::
+ When a test point value is ``None``, consistency is delegated to the underlying BSA::
sage: K. = ParametricRealField([2, 1], big_cells=True, mutable_values=True, allow_refinement=False)
sage: assert b>0
sage: K.remove_test_point()
sage: K.assume_comparison(b.sym(), operator.lt, 0)
- Traceback (most recent call last):
- ...
- ParametricRealFieldInconsistencyError...
+ sage: K._bsa
+ BasicSemialgebraicSet_veronese(BasicSemialgebraicSet_polyhedral_ppl_NNC_Polyhedron(Constraint_System {-1==0}, names=[x0]), polynomial_map=[b])
User code should not rely on whether assumptions regarding
the nonvanishing of denominators are recorded::
@@ -1065,7 +1052,7 @@ class ParametricRealField(Field):
eq_factors.append(new_fac)
except FactorUndetermined:
eq_factors.append(new_fac) #??? or pass?
- #This doesn't matter for now because under this branch self._allow_refinement=True, and testpoint value is not removed, so the exception FactorUndetermined should not happen.
+ #This doesn't matter for now because under this branch self._allow_refinement=True, and testpoint value is not removed, so the exception FactorUndetermined should not happen.
#In the future if the code in big_cells_impl changes and this exception happens, it is still safer to eq_factors.append(new_fac). T
#This could result in smaller cell (which is allowed). With "pass" it may raise error in corner cases, such as when the removed testpoint value was the only eq factor without which there would be a sign contradiction.
for new_fac in lt_factors:
diff --git a/cutgeneratingfunctionology/shared/EvaluationExceptions.py b/cutgeneratingfunctionology/shared/EvaluationExceptions.py
deleted file mode 100644
index 7e7dd6e04..000000000
--- a/cutgeneratingfunctionology/shared/EvaluationExceptions.py
+++ /dev/null
@@ -1,2 +0,0 @@
-class FactorUndetermined(Exception): # FactorUndetermined is raised when an expression can not be evaluated with a test point.
- pass
\ No newline at end of file
diff --git a/cutgeneratingfunctionology/spam/exceptions.py b/cutgeneratingfunctionology/spam/exceptions.py
new file mode 100644
index 000000000..997328c34
--- /dev/null
+++ b/cutgeneratingfunctionology/spam/exceptions.py
@@ -0,0 +1,14 @@
+class FactorUndetermined(Exception): # FactorUndetermined is raised when an expression can not be evaluated with a test point.
+ pass
+
+
+class ParametricRealFieldFrozenError(ValueError):
+ pass
+
+
+class ParametricRealFieldInconsistencyError(ValueError):
+ pass
+
+
+class ParametricRealFieldRefinementError(ValueError):
+ pass
diff --git a/cutgeneratingfunctionology/spam/parametric_real_field_element.py b/cutgeneratingfunctionology/spam/parametric_real_field_element.py
index a0cd5c55e..0d9bb2e86 100644
--- a/cutgeneratingfunctionology/spam/parametric_real_field_element.py
+++ b/cutgeneratingfunctionology/spam/parametric_real_field_element.py
@@ -9,7 +9,7 @@
from sage.rings.real_mpfr import RR
from sage.functions.other import ceil, floor
from sage.functions.generalized import sign
-from cutgeneratingfunctionology.shared.EvaluationExceptions import FactorUndetermined
+from cutgeneratingfunctionology.spam.exceptions import FactorUndetermined
import operator