Source code for prodigy.analysis.evtinvariants.heuristics.positivity.dominat_root_rejection
import logging
from typing import Optional
import sympy
from prodigy.analysis.evtinvariants.heuristics.positivity.polynomial_positivity import FinitePolynomialPositivity
from prodigy.analysis.evtinvariants.heuristics.positivity.positivity import PositivityHeuristic
from prodigy.analysis.exceptions import VerificationError
from prodigy.util.logger import log_setup
[docs]
class DominantRootHeuristic(PositivityHeuristic):
"""
This heuristic tries to falsify positivity for univariate rational functions by using results about dominant
roots in the characteristic polynomial of the linear recurrence sequence related to it.
More details can be found in https://arxiv.org/pdf/1309.1550.pdf in particular Proposition 2.
"""
logger = log_setup("DominantRootHeuristic", logging.DEBUG)
def _is_positive(self, f: str) -> Optional[bool]:
s_f = sympy.S(f)
if len(s_f.free_symbols) > 1:
raise VerificationError(f"Currently only univariate rational functions are supported. Given {f}")
if not s_f.is_rational_function():
raise VerificationError(f"The heuristic does not work on non-rational funtions. Given {f}")
self.logger.debug("Put rational function into canoncial form.")
s_f = s_f.cancel()
if s_f.is_polynomial():
# It was not a proper rational function but a polynomial instead.
return FinitePolynomialPositivity().is_positive(str(s_f))
self.logger.debug("Cancelled fraction: %s", s_f)
s_num, s_denom = s_f.as_numer_denom()
print(f"Remainder: {sympy.prem(s_num, s_denom)}")
s_denom_as_poly = sympy.Poly(s_denom.as_poly().all_coeffs()[::-1], *s_f.free_symbols)
self.logger.debug("Characteristic Polynomial of LRS: %s", s_denom_as_poly)
real_roots = s_denom_as_poly.real_roots()
if len(real_roots) == 0:
# No dominant real root -> thus there definetly is a negative sign somewhere.
self.logger.debug("No real roots found. Not positive!")
return False
else:
# We have at least one real root.
self.logger.debug("Real roots of polynomial: %s", real_roots)
all_roots = s_denom_as_poly.all_roots()
self.logger.debug("All roots of polynomial: %s", all_roots)
max_all_roots = max(sympy.Abs(root) for root in all_roots)
max_real_roots = max(sympy.Abs(root) for root in real_roots)
if max_real_roots >= max_all_roots:
# real roots are dominant.
self.logger.debug("We have at least one real dominant root. -> Result UNKNOWN")
return None
else:
# No real dominant root. The LRS has infinitely many negative coefficients.
self.logger.debug("No real dominant root. -> Not positive!")
return False