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 1/9] 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 2/9] 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 3/9] 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 4/9] 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 5/9] 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 6/9] 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 7/9] 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 8/9] 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 9/9] 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: