diff --git a/cutgeneratingfunctionology/igp/parametric.sage b/cutgeneratingfunctionology/igp/parametric.sage index 908b10e9a..631226716 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 @@ -158,26 +156,23 @@ 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 - sage: K.assume_comparison(a.sym(), operator.le, 3) + True + sage: K. = ParametricRealField(None, mutable_values=True) + sage: K.assume_comparison(a.sym(), operator.le, 2) - 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 + True sage: b <= 11 True - """ Element = ParametricRealFieldElement @@ -318,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... """ @@ -410,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 ] @@ -562,6 +555,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 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): """ @@ -854,6 +869,13 @@ class ParametricRealField(Field): 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 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, 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/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..b50c7d8d1 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): @@ -77,8 +78,12 @@ def val(self): try: return self._val except AttributeError: - return self.parent()._eval_factor(self._sym) - + 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:: @@ -126,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: # Partial evauation is happen, assume the result is True. + result = True if result: true_op = op else: