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: